Compaq OpenVMS
デバッガ説明書


前へ 次へ 目次 索引


  1. main()の最初のいくつかの文では,スレッドが使用する同期化オブジェクトと条件変数に対応する述語が初期化される。それらの同期化オブジェクトは省略時の属性により初期化される。条件変数の述語は,述語のまわりをループしているスレッドがループし続けるように初期化される。プログラムのこの箇所で SHOW TASK/ALL を実行すれば,%TASK 1 が表示される。

  2. ワーカ・スレッド %TASK 2 と %TASK 3 が作成される。ここで作成された各スレッドは同じ起動ルーチン(worker_routine)を実行するので,pthread_create に対する同じ呼び出しを再使用できる。ただし,異なるスレッド ID を格納するためにわずかな違いがある。それらのスレッドは省略時の属性を使用して作成され,この例では使用されない引数を引き渡される。

  3. 条件変数に対応する述語がブロードキャストの準備のためクリアされる。この結果,条件変数によってウェイクアップされるスレッドは正しくウェイクアップされ,間違ってウェイクアップされることはない。述語をクリアすると,条件変数がブロードキャスト済みまたはシグナル通知済みとなっているので,新しいスレッドが条件変数を待つこともなくなる。期待通りの効果が得られるかどうかは,行 3893 の条件待ち呼び出しのコーディングが正しいかどうかによるが,この例のコーディングは間違っている。

  4. 初期スレッドはほとんどすぐにブロードキャスト呼び出しを実行するので,どのワーカ・スレッドもまだ条件待ちをしていない。ブロードキャストにより,その時点でその条件変数を待っているすべてのスレッドがウェイクアップされる。

    プログラマは,ブロードキャスト時にすべてのスレッドが条件変数を待っているようにするか,またはブロードキャストがすでに起こったことを対応する述語で明らかにすることによって,そのブロードキャストが確実に認識されるようにしなければならない。このような方法は,この例では意図的に省いている。

  5. 初期スレッドは,ワーカ・スレッドがどちらも正しく終了したことを確かめるために,両者を結合しようとする。

  6. ワーカ・スレッドが worker_routine を実行すると,大量の計算に時間がかかる。そのため初期スレッドは,どちらのワーカ・スレッドも条件変数を待つ準備ができていないときにその条件変数をブロードキャストする。

  7. 次にワーカ・スレッドは pthread_cond_wait 呼び出しを実行し,必要に応じて呼び出しのまわりでロックを行う。両方のワーカ・スレッドがブロードキャストを検出できなくてブロックするのはこの箇所である。そのときに SHOW TASK/ALL コマンドを入力すれば,両方のワーカ・スレッドが条件変数を待っていることが分かる。このようにプログラムがデッドロック状態になったときに制御をデバッガに戻すには,Ctrl/C を押さなければならない。

デバッガを使用すればスレッドの相対的な実行を制御することにより,例 17-1 のような問題を診断することができます。この例の場合は,初期スレッドの実行を中断してワーカ・スレッドに計算を終了させ,ワーカ・スレッドがブロードキャスト時に条件変数を待っているようにできます。その手順は次のとおりです。

  1. デバッグ・セッションの開始時に,ブロードキャストの直前で初期スレッドの実行が中断するよう,行3836にブレークポイントを設定する。

  2. 初期スレッドを実行しワーカ・スレッドを作成するGOコマンドを入力する。

  3. すべてのスレッドの実行を中断するこのブレークポイントで,SET TASK/HOLD %TASK 1 コマンドによって初期スレッドを保留する。

  4. ワーカ・スレッドが実行を続けるように GO コマンドを入力する。初期スレッドは保留され,実行できない。

  5. ワーカ・スレッドが条件変数をブロックしているときは,その時点で Ctrl/C を押せば制御はデバッガに戻る。SHOW TASK/ALL コマンドを実行すれば,両方のワーカ・スレッドが条件待ち副次状態で中断していることが示される。示されない場合は,それらのワーカ・スレッドを実行する GO コマンドを入力し,Ctrl/C を押してから SHOW TASK/ALL を入力する。両方のワーカ・スレッドが条件待ち副次状態になるまでこの手順を繰り返す。

  6. 最初に SET TASK/NOHOLD %TASK 1 コマンド,次に初期スレッドが実行を再開してブロードキャストを行うように,GO コマンドを入力する。これで,ワーカ・スレッドは結合し正常終了する。

17.2.2 Adaのタスキング・プログラムの例

例 17-2 はデバッグ中のタスキング・プログラムによくあるエラーを示します。この例のプロシージャ BREAK の呼び出しは,ブレークポイントを設定したり各タスクの状態を観察する候補箇所です。この例をデバッガ制御の下で実行する場合は,プロシージャ BREAK の各呼び出しの箇所で次のコマンドを入力してブレークポイントを設定し,それぞれのブレークポイントで各タスクのそのときの状態を表示できます。


DBG> SET BREAK %LINE  46 DO(SHOW TASK/ALL)
DBG> SET BREAK %LINE  71 DO(SHOW TASK/ALL)
DBG> SET BREAK %LINE  76 DO(SHOW TASK/ALL)
DBG> SET BREAK %LINE  92 DO(SHOW TASK/ALL)
DBG> SET BREAK %LINE 100 DO(SHOW TASK/ALL)
DBG> SET BREAK %LINE 104 DO(SHOW TASK/ALL)
DBG> SET BREAK %LINE 120 DO(SHOW TASK/ALL)

このプログラムでは次の 4 つのタスクが作成されます。

例 17-2 Adaのタスキング・プログラムの例

 1 -- Tasking program that demonstrates various tasking conditions. 
 2 
 3 package TASK_EXAMPLE_PKG is 
 4    procedure BREAK; 
 5 end; 
 6 
 7 package body TASK_EXAMPLE_PKG is 
 8    procedure BREAK is 
 9    begin 
 10      null; 
 11   end; 
 12 end; 
 13 
 14 
 15 with TEXT_IO; use TEXT_IO; 
 16 with TASK_EXAMPLE_PKG; use TASK_EXAMPLE_PKG; 
 17 procedure TASK_EXAMPLE is (1)  
 18 
 19    pragma TIME_SLICE(0.0); -- Disable time slicing. (2)
 20 
 21    task type FATHER_TYPE is 
 22       entry START; 
 23       entry RENDEZVOUS; 
 24       entry BOGUS; -- Never accepted, caller deadlocks. 
 25    end FATHER_TYPE; 
 26 
 27    FATHER : FATHER_TYPE; (3)
 28 
 29    task body FATHER_TYPE is 
 30       SOME_ERROR : exception; 
 31 
 32       task CHILD is  (4)
 33          entry E; 
 34       end CHILD; 
 35 
 36       task body CHILD is 
 37       begin 
 38          FATHER_TYPE.BOGUS;   -- Deadlocks on call to its parent 
 39       end CHILD;              --(parent does not have an accept 
 40                               -- statement for entry BOGUS). Whenever 
 41                               -- a task-type name(here, FATHER_TYPE)
 42                               -- is used within a task body, the 
 43                               -- name designates the task currently 
 44                               -- executing the body. 
 45    begin --(of FATHER_TYPE body) 
 46 
 47       accept START do    
 48          BREAK;   -- Main program is waiting for this rendezvous to 
 49                   -- complete; CHILD is suspended when it calls the 
 50                   -- entry BOGUS. 
 51          null; 
 52       end START; 
 53 
 54       PUT_LINE("FATHER is now active and"); (5)  
 55       PUT_LINE("is going to rendezvous with main program."); 
 56 
 57       for I in 1..2 loop 
 58          select 
 59             accept RENDEZVOUS do 
 60                PUT_LINE("FATHER now in rendezvous with main program"); 
 61             end RENDEZVOUS; 
 62          or 
 63             terminate; 
 64          end select; 
 65 
 66          if I = 2 then 
 67             raise SOME_ERROR; 
 68          end if; 
 69       end loop; 
 70 
 71    exception   
 72       when OTHERS => 
 73          BREAK;   -- CHILD is suspended on entry call to BOGUS. 
 74        -- Main program is going to delay while FATHER 
 75                   -- terminates. 
 76                   -- MOTHER is ready to begin executing. 
 77          abort CHILD; 
 78          BREAK;   -- CHILD is now abnormal due to the abort statement. 
 79 
 80          raise; -- SOME_ERROR exception terminates FATHER. 
 81    end FATHER_TYPE; 
 82 
 83 begin    --(of TASK_EXAMPLE) (6)  
 84 
 85    declare 
 86       task MOTHER is  (7)
 87          entry START; 
 88          pragma PRIORITY(6); 
 89       end MOTHER; 
 90 
 91       task body MOTHER is 
 92       begin 
 93          accept START; 
 94          BREAK;   -- At this point, the main program is waiting for 
 95                   -- its dependents(FATHER and MOTHER)to terminate. 
 96                   -- FATHER is terminated. 
 97          null; 
 98       end MOTHER; 
 99    begin  (8)
100 
101 
102       BREAK;   -- FATHER is suspended at accept start. 
103                -- CHILD is suspended in its deadlock. 
104                -- MOTHER has activated and ready to begin executing. 
105       FATHER.START;  (9)        
106       BREAK;   -- FATHER is suspended at its 'select or terminate' 
107                -- statement. 
108 
109 
110       FATHER.RENDEZVOUS;  
111       FATHER.RENDEZVOUS;  (10)
112       loop  (11)               
113          -- This loop causes the main program to busy wait 
114          -- for the termination of FATHER, so that FATHER 
115          -- can be observed in its terminated state. 
116          if FATHER'TERMINATED then 
117             exit; 
118          end if;       
119          delay 1.0; 
120       end loop; 
121 
122       BREAK;   -- FATHER has terminated by now with the unhandled 
123                -- exception SOME_ERROR. CHILD no longer exists 
124                -- because its master(FATHER)has terminated. Task 
125                -- MOTHER is ready. 
126       MOTHER.START; (12)     
127          -- The main program enters a wait-for-dependents state, 
128          -- so that MOTHER can finish executing. 
129    end; 
130 end TASK_EXAMPLE; (13)

次の番号は,例 17-2 の番号に対応しています。

  1. すべての Ada ライブラリ・パッケージ(この場合は TEXT_IO)の作成が終わると,メイン・プログラムが自動的に呼び出されてその宣言部分の作成が開始される(行 5 〜 82)。

  2. この例では,実行のつど同じ処理が行われるように,タイム・スライス機能(第 17.5.2 項 を参照)は使用していない。プラグマ TIME_SLICE の値が 0.0 になっているのは,プロシージャ TASK_EXAMPLE のためにタイム・スライス機能を禁止する必要があることを示している。

    VAX プロセッサでは,プラグマ TIME_SLICE を省略するか値 0.0 を指定すると,タイム・スライス機能は禁止される。

    Alpha プロセッサでは,タイム・スライス機能を禁止するためにプラグマ TIME_SLICE(0.0)を使用しなければならない。

  3. タスク・オブジェクト FATHER が作成され,%TASK 2 と指定されたタスクが作成される。FATHER にはプラグマ PRIORITY がないので,省略時の優先順位が与えられる。FATHER(%TASK 2)は中断状態で作成され,Ada の規則に従ってメイン・プログラムの文部分が開始されて初めて起動されます(行 83)。行 29 〜 81 のタスク本体の作成では,FATHER_TYPE 型のタスクが実行する文が定義されている。

  4. タスク FATHER はタスク CHILD を宣言する(行 32)。1 つのタスクは,1 つのタスク・オブジェクトであり,なにか 1 つのタスク型を表す。タスク CHILD が作成され,起動されるのは,FATHER が起動されてからである。

  5. 非同期システム・トラップ(AST)を引き起こすのは,この TEXT_IO の一連の PUT_LINE 文だけである。入出力(I/O)の終了により AST が実行要求される。

  6. タスクの FATHER は,メイン・プログラムが待っているときに並行して起動される。FATHER にはプラグマ PRIORITY がないので,省略時の優先順位 7 を与えられる(省略時の優先順位については『DEC Ada Language Reference Manual』を参照)。FATHER の起動は行 29 〜 44 で作成される。

    起動されたタスク FATHER は,タスク CHILD が起動され,%TASK 3 を指定されたタスクが作成されるのを待つ。CHILD は行 38 で 1 つのエントリ呼び出しを実行し,そのエントリが受け付けられないのでデッドロックになる(第 17.7.1 項 を参照)。

    タイム・スライス機能が禁止され,優先順位の高い実行可能なタスクがないので,FATHER は起動後も行 47 の ACCEPT 文でブロックされるまで実行される。

  7. タスク MOTHER が定義され,%TASK 4 と指定されたタスクが作成される。プラグマ PRIORITY により,MOTHER には優先順位 6 が与えられる。

  8. タスク MOTHER が起動し,行 91 を実行する。起動後,メイン・プログラム(%TASK 1)の実行再開が可能になる。%TASK 1 は省略時の優先順位が7であり,MOTHER の優先順位より高いので,メイン・プログラムの実行が再開される。

  9. メイン・プログラムとタスク FATHER の最初のランデブ。この後,FATHER は行 58 の TARMINATO 文の SELECT で中断される。

  10. FATHER との 3 回目のランデブでは,FATHER は行 67 で SOME_ERROR という例外を発生させる。ハンドラは行 72 でその例外を捉え,中断しているタスク CHILD を強制終了してから,再び例外を発生させる。その後,FATHER は終了する。

  11. delay 文で指定されたループにより,制御が行 122 に到達するときには FATHER は終了するのに十分なほど先まで実行されている。

  12. このエントリ呼び出しにより,MOTHER は行 93 のランデブ待ちを解除される。MOTHER はその accept 文(その他の文は含まない)を実行し,ランデブは終了する。すると,優先順位が 6 にすぎない MOTHER は行 94 で制御を奪われる。

  13. メイン・プログラム(%TASK 1)は MOTHER とのランデブ後,行 127 〜 129 を実行する。メイン・プログラムは行 129 で,自分のすべての依存タスクの終了を待たなければならない(第 17.6.4 項 を参照)。メイン・プログラムが行 129 に到達するとき,まだ終了していないタスクは MOTHER だけである。MOTHER は,行 97 の 文が実行されるまでは終了できない。MOTHER は行 98 で実行を終了する。すべてのタスクが終了したので,メイン・プログラムは実行を終了する。メイン・プログラムから制御が戻されて,コマンド行インタプリタの実行が再開される。

17.3 デバッガ・コマンドによるタスクの指定

タスク とは,その他のタスクと並行して実行される要素です。タスクには固有のタスク ID(第 17.3.3 項 を参照),独立したスタック,および独立したレジスタ・セットが与えられます。

アクティブ・タスクと可視タスクの現在の定義により,タスク操作のコンテキストが決まります。第 17.3.1 項 を参照してください。

デバッガ・コマンドにタスクを指定するときには,次のいずれかの形式で指定できます。

17.3.1 アクティブ・タスクと可視タスクの定義

アクティブ・タスクとは,STEP,GO,CALL,または EXIT コマンドを実行したときに起動されるタスクです。プログラムをデバッガの制御下に置くと,最初はアクティブ・タスクの中で実行が中断されます。デバッグ・セッション中にアクティブ・タスクを変更するには SET TASK/ACTIVE コマンドを使用します。

注意

SET TASK/ACTIVE コマンドは,POSIX Threads 経由で実行するタスキング,POSIX Threads(OpenVMS Alpha システムと VAX システムの両方で)と Ada(OpenVMS Alpha システムで)の両方で動作しません。POSIX Threads で照会型のアクションを行うときは,SET TASK/ACTIVE コマンドの代わりに,SET TASK/VISIBLE コマンドを使用します。特定のスレッドでステップを制御したいときは,ブレークポイントを適切な位置に配置します。

次のコマンドでは CHILD というタスクがアクティブ・タスクになります。


DBG> SET TASK/ACTIVE CHILD

可視タスク とは,スタックとレジスタ・セットがデバッガによって現在のコンテキストとして使用されるタスクです。デバッガはシンボル,レジスタ値,ルーチン呼び出し,ブレークポイントなどを参照するときにスタックとレジスタ・セットを使用します。たとえば,次のコマンドでは,可視タスクのコンテキストの変数 KEEP_COUNT の値が表示されます。


DBG> EXAMINE KEEP_COUNT

最初は,可視タスクがアクティブ・タスクです。可視タスクを変更するには,SET TASK/VISIBLE コマンドを使用します。このコマンドにより,アクティブ・タスクに影響を与えずにその他のタスクの状態を参照することができます。

デバッガ・コマンドに組み込みシンボルの %ACTIVE_TASK と %VISIBLE_TASK を使用することにより,それぞれアクティブ・タスクと可視タスクを指定できます(第 17.3.4 項 を参照)。

SET TASK コマンドによるタスク特性の変更についての詳しい説明は,第 17.5 節 を参照してください。

17.3.2 Adaのタスキングの構文

タスクを宣言するには,単一タスクを宣言するか,またはあるタスク型のオブジェクトを宣言します。次に例を示します。


-- タスク型の宣言。
-- 
task type FATHER_TYPE is
...
end FATHER_TYPE; 
 
task body FATHER_TYPE is
...
end FATHER_TYPE; 
 
-- 単一タスク。
-- 
task MOTHER is
...
end MOTHER; 
 
task body MOTHER is
...
end MOTHER; 

タスク・オブジェクト とは,タスク値を含むデータ項目です。タスク・オブジェクトが作成されるのは,プログラムによって単一タスクかタスク・オブジェクトが作成されるとき,タスク構成要素を含んでいる配列かレコードをユーザが宣言するとき,またはタスク・アロケータが評価されるときです。次に例を示します。


-- タスク・オブジェクトの宣言。
-- 
FATHER : FATHER_TYPE; 
 
-- タスク・オブジェクト(T)はレコードの構成要素。
-- 
type SOME_RECORD_TYPE is
   record
      A, B: INTEGER; 
      T   : FATHER_TYPE; 
   end record; 
 
HAS_TASK : SOME_RECORD_TYPE; 
 
-- タスク・オブジェクト(POINTER1)はアロケータを通じて作成される。
-- 
type A is access FATHER_TYPE; 
POINTER1 : A := new FATHER_TYPE; 

タスク・オブジェクトは,その他のオブジェクトに似ています。デバッガ・コマンドにタスク・オブジェクトを指定するときには,名前かパス名を指定します。次に例を示します。


DBG> EXAMINE FATHER
DBG> EXAMINE FATHER_TYPE$TASK_BODY.CHILD

タスク・オブジェクトを作成すると,Compaq Ada 実行時ライブラリによってタスクが作成され,そのタスク・オブジェクトにタスク値が割り当てられます。タスク・オブジェクトの値はその他の Ada オブジェクトと同じく,オブジェクトが初期化されるまでは未定義になるので,初期化されていない値を使用するとその結果は予測できません。

あるタスク型または単一タスクの タスク本体 は,1 つのプロシージャとして Compaq Ada の中に組み込まれます。そのプロシージャはその型のタスクが起動されるとき,Compaq Ada の実行時ライブラリから呼び出されます。デバッガはタスク本体を普通のAdaプロシージャとして処理します。特別な構造の名前を持っている点が異なります。

デバッガ・コマンドにタスク本体を指定するには,次の構文を使用して,タスク型として宣言されているタスクを指定します。


task-type-identifier$TASK_BODY 

単一タスクの指定には,次の構文を使用します。


task-identifier$TASK_BODY 

たとえば,次のように指定します。


DBG> SET BREAK FATHER_TYPE$TASK_BODY

デバッガはタスク依存の Ada 属性 T'CALLABLE,E'COUNT,T'STORAGE_SIZE,および T'TERMINATED はサポートしません。このうち,T はタスク型,E はタスク・エントリです(これらの属性についての詳しい説明は,Compaq Ada の資料を参照してください)。EVALUATE CHILD'CALLABLE などのコマンドは入力できません。しかし,デバッガの SHOW TASK コマンドを使用してこれらの属性の内容を知ることはできます。詳しい説明は,第 17.4 節 を参照してください。


前へ 次へ 目次 索引