パラレルmakeがハングする理由を調べてみる…再起動-2歩下がって3歩進む-

どうもMSYSのバグが濃厚になってきたので、カテゴリにMSYSを入れてみた…
まずはハングするサンプルプログラムを作ろうと思ったが、中々ハングしないので、いったん諦めて、Makefileの軽量化に取り組んでみた。
最大ジョブ数を2(-j2)にして、同時に動作するmakeツリーを3パス作ったら、それだけでmakeがハングした。同じMakefileを-j1(パラレルmake無効)で実行すると、問題なく動作する。どんだけしょぼいんだコイツわorz。で、ログをチェックすると、やはりCHLDシグナルを捕捉できておらず、readで止まっている。
MSYS DLLのログ量を調節しながら何が起きているかを調べてみるが、makeがごちょごちょ色々な事をしているので、ログが複雑になり手が付けられない。と言うか、MSYS DLLって奇妙なコードが多くて走るコードを追っかけきれん(゚Д゚;)
かろうじて読み取れたのは…

  • MSYS DLLは子プロセスが終了したことを把握している(当たり前)。
  • makeに対してCHLDシグナルを送る最初の処理(シグナル送信登録)はしてある。
  • makeの延長で動いているMSYSは、CHLDシグナルが到着していることを確認しており、ディスパッチをした。
  • makeにCHLDシグナルを届けるためのイベント登録も完了している。
  • でもなぜかmakeにCHLDシグナルが上がってこない。
  • MSYS DLLは、1回目のCHLDシグナルをディスパッチしたが、実際にはmakeがCHLDシグナルを受け取っていないため、2個目の子プロセスが終了したときに発生したCHLDシグナルをディスパッチできず、保留状態になっている。
  • 2個目のCHLDシグナルが保留状態のままになっているが、MSYS DLLはその状態が解除されるものだと思って、無限にループ処理を行っている→つまりハングアップ

…では、シリアルmakeは何故動くのかと疑問が浮上し、再びmakeのソース&ログに戻って、実行パスを追ってみる……あれ?CHLDシグナル受け取ってるよ??頭を冷やして、シリアルmakeとパラレルmakeの実行パスを改めて追っかけ直す。すると、子プロセスを実行するところは、シリアルmakeもパラレルmakeも同じ処理を使っておりvfork()→execv()となっていたが、子プロセスの終了を待つ処理は、パラレルmakeではreadで待っているのに対して、シリアルmakeではwaitで待っている。んんん??
三度頭を冷やして、シリアルmakeとパラレルmakeの違いをサンプルプログラムに反映させていく…。おや、再現できた。

  • 子プロセスは1個だけで再現可能。
  • 子プロセスの終了をwaitで待つとCHLDシグナルを捕捉でき、waitが返ってくる。
  • 子プロセスの終了を「stdinに対する」readで待つとCHLDシグナルを捕捉でき、readが返ってくる。
  • 子プロセスの終了を「pipeに対する」readで待つとCHLDシグナルが捕捉できず、またreadが返ることもない。
  • MSYS DLLをv1.0.17に戻すと、子プロセスの終了を「pipeに対する」readで待ってもCHLDシグナルを捕捉でき、readが返ってくる。

つまり、pipeに対するreadでの待ちケースに、MSYS DLL v1.0.17とMSYS DLL v1.0.18で、どのような実装の違いがあるのかを調べれば解決策が見えてくる(かも)。ん〜ようやく尻尾が見えてきた!