Fixing precompilation hangs due to open tasks or IO
На Julia 1.10 или выше вы можете увидеть следующее сообщение:
Это может повторяться. Если это продолжает повторяться без намеков на то, что это разрешится само собой, у вас может быть "зависание предварительной компиляции", которое требует исправления. Даже если это временно, вы можете предпочесть решить эту проблему, чтобы пользователи не беспокоились об этом предупреждении. Эта страница проведет вас через процесс анализа и исправления таких проблем.
Если вы следуете совету и нажимаете Ctrl-C
, вы можете увидеть
^C Interrupted: Exiting precompilation...
1 dependency had warnings during precompilation:
┌ Test1 [ac89d554-e2ba-40bc-bc5c-de68b658c982]
│ [pid 2745] waiting for IO to finish:
│ Handle type uv_handle_t->data
│ timer 0x55580decd1e0->0x7f94c3a4c340
Это сообщение передает две ключевые информации:
- зависание происходит во время предварительной компиляции
Test1
, зависимостиTest2
(пакета, который мы пытались загрузить с помощьюusing Test2
) - во время предварительной компиляции
Test1
Julia создала объектTimer
(используйте?Timer
, если вы не знакомы с таймерами), который все еще открыт; пока он не закроется, процесс завис.
Если этого достаточно, чтобы вы поняли, как создается timer = Timer(args...)
, одно хорошее решение — добавить wait(timer)
, если timer
в конечном итоге завершится сам по себе, или close(timer)
, если вам нужно принудительно закрыть его, перед финальным end
модуля.
Однако есть случаи, которые могут быть не такими простыми. Обычно лучшим вариантом является начать с определения, вызвано ли зависание кодом в Test1 или одной из зависимостей Test1:
- Опция 1:
Pkg.add("Aqua")
и используйтеAqua.test_persistent_tasks
. Это должно помочь вам определить, какой пакет вызывает проблему, после чего следует выполнить инструкции below. Если необходимо, вы можете создатьPkgId
какBase.PkgId(UUID("..."), "Test1")
, где...
берется из записиuuid
вTest1/Project.toml
. - Вариант 2: вручную диагностировать источник зависания.
Чтобы вручную диагностировать:
Pkg.develop("Test1")
- Закомментируйте весь код,
включенный
или определенный вTest1
, кроме операторовusing/import
. - Попробуйте снова
using Test2
(или дажеusing Test1
, предполагая, что это тоже зависает)
Теперь мы подошли к развилке: либо
- висит, указывая на то, что это due to one of your dependencies
- вис hanging исчезает, указывая на то, что это due to something in your code.
Diagnosing and fixing hangs due to a package dependency
Используйте бинарный поиск, чтобы определить проблемную зависимость: начните с закомментирования половины ваших зависимостей, затем, когда вы изолируете, какая половина отвечает, закомментируйте половину этой половины и так далее. (Вам не нужно удалять их из проекта, просто закомментируйте операторы using
/import
.)
Как только вы идентифицировали подозреваемый пакет (здесь мы будем называть его ThePackageYouThinkIsCausingTheProblem
), сначала попробуйте предварительно скомпилировать этот пакет. Если он также зависает во время предварительной компиляции, продолжайте искать проблему в обратном направлении.
Однако, скорее всего, ThePackageYouThinkIsCausingTheProblem
будет предварительно скомпилирован нормально. Это предполагает, что проблема в функции ThePackageYouThinkIsCausingTheProblem.__init__
, которая не выполняется во время предварительной компиляции ThePackageYouThinkIsCausingTheProblem
, но выполняется в любом пакете, который загружает ThePackageYouThinkIsCausingTheProblem
. Чтобы протестировать эту теорию, настройте минимальный рабочий пример (MWE), что-то вроде
(@v1.10) pkg> generate MWE
Generating project MWE:
MWE\Project.toml
MWE\src\MWE.jl
где исходный код MWE.jl
находится
module MWE
using ThePackageYouThinkIsCausingTheProblem
end
и вы добавили ThePackageYouThinkIsCausingTheProblem
в зависимости MWE.
Если этот минимальный рабочий пример воспроизводит зависание, вы нашли своего виновника: ThePackageYouThinkIsCausingTheProblem.__init__
должен создавать объект Timer
. Если объект таймера можно безопасно закрыть, это хороший вариант. В противном случае наиболее распространенным решением является избегание создания таймера, пока любой пакет компилируется заранее: добавьте
ccall(:jl_generating_output, Cint, ()) == 1 && return nothing
как первая строка ThePackageYouThinkIsCausingTheProblem.__init__
, и это позволит избежать инициализации в любом процессе Julia, целью которого является предварительная компиляция пакетов.
Fixing package code to avoid hangs
Поиск в вашем пакете на предмет suggestive слов (например, "Timer") и посмотрите, можете ли вы определить, где возникает проблема. Обратите внимание, что определение метода, как
maketimer() = Timer(timer -> println("hi"), 0; interval=1)
не является проблемой само по себе: это может вызвать эту проблему только если maketimer
вызывается во время определения модуля. Это может происходить из верхнего уровня, например, из такого выражения как
const GLOBAL_TIMER = maketimer()
или это может произойти в precompile workload.
Если вам трудно определить причинные строки, подумайте о том, чтобы выполнить бинарный поиск: закомментируйте разделы вашего пакета (или используйте include
, чтобы исключить целые файлы) до тех пор, пока вы не сузите проблему.