図3.8に示された簡単なfork-joinプロトコルを考えてみよう.
マスタスレッド(master
)は多数の子スレッド
(task
)をforkし, 全ての作られたスレッドが終わるのを
待つ. マスタと全ての子は終わっていないタスクの
数を保持するカウンタを共有する. マスタは子スレッドを
ただ単にCの手続き task
をループ内で呼ぶことで fork し,
完了を待つために join_children
を呼び出す.
各タスクはここでは重要ではない何かの計算を行ない,
計算中にブロックするかもしれず, そして, 終了したときには,
最終的には signal_finish
を呼び出すだろう.
結果として, マスタが join_children
を
試みたときにいくつかのタスクがまだ未完了であるときに
マスタはブロックする.
基本的には2つのシナリオがある. もし実際にブロックする子が
ないときには, task
への各手続き呼び出しは
マスタに返ることなしに, signal_finish
を
呼び出す. signal_finish
への全ての呼びだしは単純に
カウンタを一つづつ減らすだけである. マスタは
join_children
のカウンタをチェックし
カウンタがすでにゼロであるのを発見し,
そのまま実行を継続する.
一方, もし子のどれかがブロックしたときには,
マスタが join_children
によってカウンタを
チェックしたときに, カウンタはゼロでないかもしれない.
その場合は, マスタはヒープコンテキストを確保し,
そのカウンタにコンテキストを書き込み, 自分を
ブロックさせるために switch_to_parent
を
呼び出す. 最後の子が signal_finish
を呼ぶ時,
その子はカウンタの中にコンテキストが書かれているのを
発見し, マスタを再開するだろう.
この例は単純なケースにおいて,
StackThreads のプリミティブ上でどのように同期オペレーション(例えば,
コンテキストスイッチを起動するオペレーション)を設計, 実装するのか
を解説している.
この例では, join_children
は同期オペレーションである.
一般に, 同期オペレーションを呼び出すスレッドは
そのスレッドのフレームの情報を td によって指される領域に置き,
td を同期オペレーションに渡す.
SET_THREAD_DESC(
td)
を呼び出さなければならない.
同期オペレーションは同期条件をチェックし, ブロックすると
決めたときには, リンクによって指される領域をその手続きの
フレームの情報と呼び出しの鎖の一つ前のフレームのデスクリプタへのリンク
で満たすSET_LINK_DESC(
link, td)
を呼び出す.