この節においては, StackThreadsを用いて実現されたマルチスレッディング環境と, 逐次 C/C++ を想定した gdb とを ライン情報レベルで整合させるためには,どのようにすれば良いかを考える.
ABCL/f システム内部における StackThreads を用いた挙動については, すでに 4.2.2節で触れた通りである. 但し, マルチスレッドライブラリの挙動が gdb 上でどのように見えるのか? どの程度隠す事が出来るのか? そのためにはどのようにライン情報を処理すれば良いのか について議論する必要がある. そのためにも, この節では StackThreads のマルチスレッディングの実装に関して 手続き呼び出し関係に立ち入って議論する. 対象となるのは, future, touch, reply に加えて,remote operation (future/reply)や object 実装に関する物である.
まず,future, touch, reply に関して考察する.
一方, gdb 上で touch 命令を行った時に期待される動作は,以下の様である.
ところで,ブロック時について考えると, 実行は lib C 内の手続き D からフレームAに直接復帰している. このとき, gdb では一度その復帰を行う手続き D 内まで step実行で移動した後でなければ,ブロック時の動作を保証できない. つまり, スレッドがブロックする場合は ランタイムの実行のトレースをnext実行でスキップする事ができない. 実際には, touch 命令がランタイム関数を呼び出した時点では ブロックするかしないかがまだ判断されていないので, ランタイム関数呼び出しがあった場合はすべてstep実行で フレームCに移行する必要がある事になる.
まとめると, gdb をつかってStackThreadsの touch 命令をトレースするためには, 実際に現在のフレームの一つ前のフレームに復帰を行う 手続きまでstep実行などでトレースしなければ, touch 命令のstep/nextに相当することはできない ことになる.
このため,現在は一度ランタイム関数に突入すると ユーザが(もしくはユーザインターフェイスプログラムが) gdbに対してstep命令を繰り返す事となっている. これについては,4.6.3節の中で述べる.
また,ユーザの繁雑さを避けるため, touch 命令付近では, ランタイム関数突入時のみ step の要請をおこない, それ以外の行は4.5.2節のように, ユーザにはみえないランタイムとして設定されている. また,ランタイムライブラリ内部についても, 必要のある部分, つまり, 実際にフレーム Aへの復帰を行う手続きDに至る呼び出し行のみが 行として設定され,他のランタイム処理はやはりユーザには 見えない様になっており, 最小のstep入力で実行が継続できるようにしている.
実際に付加すべきライン情報をまとめると以下のようになる.
reply:
reply も touch 同様,一部インライン化されており,
複雑な処理の時のみ,ライブラリ関数を呼び出しその処理を行う
(図4.4).
ライブラリ内に入ってからの挙動は,
reply channel に,既に値を待っているスレッドがない場合は,
ライブラリ(フレーム B) から単に実行中スレッド(フレーム A)に復帰する.
一方,値待ちのスレッド
つまり,touchによってブロックされているスレッドが既にある場合,
その一つを取り出し,
ライブラリ(フレームB)上のスタックにそのスレッドの再開(フレーム C)を行う.
touchの場合同様,Bは実際には複数個の
手続き呼び出しフレームが積み重なっていることもある.
この場合のgdb上で期待される動作は
一方のstep 実行の場合について考える. 実際に再開すべきスレッドがあるかどうかは,ランタイム内部 でようやくわかる. よって,スレッド再開の可能性がある以上, いずれの場合においても ランタイム関数の中にstep実行を行う必要がある. そのうえで再開すべきスレッドがない場合は, フレーム Aに復帰し, ある場合は,実際に再開されたスレッドまでstep実行する必要がある.
つまり,reply/step実行の場合も, フレームA上の動作のみをgdbで行うわけには行かず, 実際にランタイム内部までstep実行を行ってもらう必要がある. 加えて,再開すべきスレッドがある場合は,その立ち上げまで フレームをstep実行で上がって行ってもらう必要がある.
実際に付加されるライン情報としては,以下のようになる.
ユーザインタフェイスレベルにおいて, システムが要請するstep実行をどのように処理するかについては, touchのとき同様, 4.6.3節の中で述べる.
次に,remote operationを呼び出される側に付いて考察する(図4.5). gdb 上においては, メッセージのスケジューリングが行われる時, その実行をトレースするかどうか指定する. このため,スレッド内でメッセージスケジューリングを行うライブラリを 呼ぶ行は, その旨を示すライン情報を付加されるようになっている.
図4.5 remote operation(receiver)における関数間呼出し関係
その後ライブラリ内ではメッセージの中身を取り出し, future, reply の実行を手続き呼び出しとして実行する. この際,さらに手続き呼び出し内に入るために, step実行をユーザに要請する事になる. その後の挙動はfuture, reply時と同じである. つまり,実際に実行が始まるスレッド内に入るため, もう一度step実行を要請する事になる. 付加すべきライン情報は, ポーリングライブラリ内のメッセージ内容の実行を 呼び出す部分にのみライン情報が入るようにすることである.
並列オブジェクトの実装に関しては単純である. ABCL/f では並列オブジェクトは中間コードとして実装されている. この際,defmethod!で定義されたメソッドを 実行するために獲得するロック機構はchannelを用いて実装されている. つまり,あらかじめchannel上には値が置かれており, メソッド開始部でロックの獲得,つまり,channelへのtouchが, 終了部でロックの解放,つまり,channelへのreplyが行われている.
よって, gdb 上で実行時には,touch, reply時の挙動がそのまま行われる. つまり,メソッド開始時touchが実行されるが,その際, ブロックする場合のみ gdb はstep実行を要請し, 一方,関数終了部においてreplyが実行されるため, そこでstep実行をした場合は, ブロックされているメソッド実行の再開をトレースできるようになる.