Gauche:MTとrequire
multi threadな場合にrequire/provideを実装するのが結構ややこしい。
とりあえず、複数のthreadから同じfeatureに対するrequireが来た場合に対応するには:
- provideされたfeatureのリストだけでなく、「requireされてload中」のfeatureの リストもキープしておく (providing リスト)。
- requireはprovidingリストを見て、featureがprovidingであれば その状態が変わるまでcondition variableで待つ。
一見、これだけで何とかなりそうだが、 requireの依存関係が循環しているとデッドロックになる可能性がある。 (もちろんそのような循環関係は不正なんだが、うっかりそのような状態に 入った時に止まるのではなくエラーにするようにしといたほうが良かろう)。
release 0.5.5での解決。load.c参照。
- システムは、globalな情報として以下のリストを持つ。
- provideされたfeatureのリスト (ldinfo.provided)
- 現在ロード中のfeatureと、それをロードしているスレッドのconsのリスト(ldinfo.providing)
- 要求されたfeatureがprovidingであるために待ちに入っているスレッドと、それが要求したfeatureのconsのリスト (ldinfo.waiting)
- あるfeatureがrequireされたら、以下のチェックを行う。
globalな情報へのアクセスは全てアトミックに行われる。
- まずldinfo.providedがサーチされる。 その中にfeatureが見つかればロードせずにreturn。
- そうでなければldinfo.providingをサーチ。そこにもfeatureが無ければ、 (cons <feature> (current-thread))をldinfo.providingにpushして、 ファイルをロードしに行く。
- そうでなければ、featureをprovidingしているthread -> がwaitingしているfeature -> がprovidingしているthread ... というチェインを辿って、 自分自身のthreadに出会うかどうか調べる。出会ったらrequireの依存関係に ループがあるということなのでエラー。
- ループが無ければ、(cons (current-thread) <feature>) をldinfo.waiting にpushしてprovidingが変化するcondition variableを待つ。
- んで、loadが正常終了するか異常終了するかしたらldinfo.providingから 自分のエントリを削除してcondition variableをsignal。