Fixing precompilation hangs due to open tasks or IO
Sur Julia 1.10 ou une version supérieure, vous pourriez voir le message suivant :
Cela peut se répéter. Si cela continue à se répéter sans indices que cela se résoudra de lui-même, vous pourriez avoir un "blocage de précompilation" qui nécessite une correction. Même si c'est transitoire, vous pourriez préférer le résoudre afin que les utilisateurs ne soient pas dérangés par cet avertissement. Cette page vous guide à travers l'analyse et la correction de tels problèmes.
Si vous suivez le conseil et appuyez sur Ctrl-C
, vous pourriez voir
^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
Ce message transmet deux informations clés :
- le blocage se produit lors de la précompilation de
Test1
, une dépendance deTest2
(le package que nous essayions de charger avecusing Test2
) - pendant la précompilation de
Test1
, Julia a créé un objetTimer
(utilisez?Timer
si vous n'êtes pas familier avec les Timers) qui est toujours ouvert ; tant qu'il n'est pas fermé, le processus est bloqué.
Si cela vous donne suffisamment d'indice pour comprendre comment timer = Timer(args...)
est créé, une bonne solution consiste à ajouter wait(timer)
si timer
se termine finalement de lui-même, ou close(timer)
si vous devez le forcer à se fermer, avant la dernière end
du module.
Cependant, il existe des cas qui peuvent ne pas être aussi simples. En général, la meilleure option est de commencer par déterminer si le blocage est dû au code dans Test1 ou s'il est dû à l'une des dépendances de Test1 :
- Option 1 :
Pkg.add("Aqua")
et utilisezAqua.test_persistent_tasks
. Cela devrait vous aider à identifier quel package cause le problème, après quoi les instructions below doivent être suivies. Si nécessaire, vous pouvez créer unPkgId
commeBase.PkgId(UUID("..."), "Test1")
, où...
provient de l'entréeuuid
dansTest1/Project.toml
. - Option 2 : diagnostiquer manuellement la source du blocage.
Pour diagnostiquer manuellement :
Pkg.develop("Test1")
- Comment out all the code
include
d or defined inTest1
, except theusing/import
statements. - Essayez à nouveau
using Test2
(ou mêmeusing Test1
en supposant que cela bloque aussi)
Maintenant, nous arrivons à un embranchement : soit
- le blocage persiste, indiquant qu'il s'agit de due to one of your dependencies
- le bloc disparaît, indiquant qu'il est due to something in your code.
Diagnosing and fixing hangs due to a package dependency
Utilisez une recherche binaire pour identifier la dépendance problématique : commencez par commenter la moitié de vos dépendances, puis lorsque vous isolez quelle moitié est responsable, commentez la moitié de cette moitié, etc. (Vous n'avez pas à les supprimer du projet, il suffit de commenter les déclarations using
/import
.)
Une fois que vous avez identifié un suspect (ici nous l'appellerons ThePackageYouThinkIsCausingTheProblem
), essayez d'abord de précompiler ce package. S'il se bloque également lors de la précompilation, continuez à rechercher le problème en arrière.
Cependant, il est très probable que ThePackageYouThinkIsCausingTheProblem
se précompile correctement. Cela suggère que le problème se situe dans la fonction ThePackageYouThinkIsCausingTheProblem.__init__
, qui ne s'exécute pas lors de la précompilation de ThePackageYouThinkIsCausingTheProblem
, mais s'exécute dans tout package qui charge ThePackageYouThinkIsCausingTheProblem
. Pour tester cette théorie, mettez en place un exemple de travail minimal (MWE), quelque chose comme
(@v1.10) pkg> generate MWE
Generating project MWE:
MWE\Project.toml
MWE\src\MWE.jl
où se trouve le code source de MWE.jl
module MWE
using ThePackageYouThinkIsCausingTheProblem
end
et vous avez ajouté ThePackageYouThinkIsCausingTheProblem
aux dépendances de MWE.
Si ce MWE reproduit le blocage, vous avez trouvé votre coupable : ThePackageYouThinkIsCausingTheProblem.__init__
doit créer l'objet Timer
. Si l'objet timer peut être fermé en toute sécurité, c'est une bonne option. Sinon, la solution la plus courante consiste à éviter de créer le timer pendant que n'importe quel package est en cours de précompilation : ajoutez
ccall(:jl_generating_output, Cint, ()) == 1 && return nothing
en tant que première ligne de ThePackageYouThinkIsCausingTheProblem.__init__
, et cela évitera toute initialisation dans tout processus Julia dont le but est de précompiler des packages.
Fixing package code to avoid hangs
Recherchez dans votre package des mots suggestifs (ici comme "Timer") et voyez si vous pouvez identifier où le problème est créé. Notez qu'une définition de méthode comme
maketimer() = Timer(timer -> println("hi"), 0; interval=1)
n'est pas problématique en soi : cela peut causer ce problème uniquement si maketimer
est appelé pendant que le module est en cours de définition. Cela pourrait se produire à partir d'une instruction de niveau supérieur telle que
const GLOBAL_TIMER = maketimer()
ou cela pourrait éventuellement se produire dans un precompile workload.
Si vous avez du mal à identifier les lignes causales, envisagez de faire une recherche binaire : commentez des sections de votre package (ou incluez
des lignes pour omettre des fichiers entiers) jusqu'à ce que vous ayez réduit le problème en portée.