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

このメッセージは二つの重要な情報を伝えています:

  • Test2using Test2で読み込もうとしている際に、依存関係であるTest1のプリコンパイル中にハングが発生しています。
  • Test1の事前コンパイル中に、Juliaはまだ開いているTimerオブジェクトを作成しました(Timersに不慣れな場合は?Timerを使用してください)。それが閉じるまで、プロセスはハングしています。

もしこれが timer = Timer(args...) がどのように作成されているかを理解するのに十分なヒントであれば、1つの良い解決策は、timer が最終的に自分で終了する場合は wait(timer) を追加するか、強制的に終了させる必要がある場合は close(timer) を追加することです。モジュールの最終 end の前に。

しかし、必ずしもそれが簡単なわけではない場合があります。通常、最良の選択肢は、ハングがTest1のコードによるものか、Test1の依存関係のいずれかによるものかを判断することから始めることです:

  • オプション 1: Pkg.add("Aqua") を使用し、Aqua.test_persistent_tasks を使用してください。これにより、どのパッケージが問題を引き起こしているかを特定するのに役立ちます。その後、指示 below に従ってください。必要に応じて、PkgIdBase.PkgId(UUID("..."), "Test1") として作成できます。ここで、...Test1/Project.tomluuid エントリから取得されます。
  • オプション2:ハングの原因を手動で診断する。

手動で診断するには:

  1. Pkg.develop("Test1")
  2. Comment out all the code included or defined in Test1, except the using/import statements.
  3. Test2を再度試してください(または、Test1を使用してもハングする場合はそれも)。

今、私たちは道の分岐点に到達しました:どちらか

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の依存関係に追加しました。

そのMWEがハングを再現する場合、あなたは原因を特定したことになります:ThePackageYouThinkIsCausingTheProblem.__init__Timerオブジェクトを作成しているに違いありません。タイマーオブジェクトを安全にcloseできる場合、それは良い選択です。そうでない場合、最も一般的な解決策は、任意のパッケージがプリコンパイルされている間にタイマーを作成しないことです:追加してください。

ccall(:jl_generating_output, Cint, ()) == 1 && return nothing

ThePackageYouThinkIsCausingTheProblem.__init__の最初の行として、パッケージをプリコンパイルする目的のJuliaプロセスでの初期化を回避します。

Fixing package code to avoid hangs

パッケージ内で示唆的な単語(ここでは「Timer」のような)を検索し、問題がどこで発生しているかを特定できるか確認してください。メソッドの定義のような

maketimer() = Timer(timer -> println("hi"), 0; interval=1)

それ自体は問題ではありません:maketimer がモジュールが定義されている間に呼び出される場合にのみ、この問題を引き起こす可能性があります。これは、次のようなトップレベルのステートメントから発生している可能性があります。

const GLOBAL_TIMER = maketimer()

または、precompile workloadで発生する可能性があります。

原因となる行を特定するのに苦労している場合は、バイナリサーチを行うことを検討してください:パッケージのセクションをコメントアウトする(または、全ファイルを省略するために include 行を使用する)ことで、問題の範囲を縮小するまで続けてください。