システム系プログラムでは、「いつ来るかわからない」「いつ終わるかわからない」といった事象(イベント)を待ち合わせることは多々あります。メインフレームでもイベントが発生あるいは終了するまで、来たか?来たか?、終わったか?終わったか?、とループしてタイミングを合わせる方法もありますが、一般のプログラムでは使いません。OSであっても、ループによるタイミング合わせは本当に必要な所だけに限定して使っています。
MVSは、イベントの完了の待ち合わせにWAIT、イベントの完了の通知にはPOSTと言うサービスを提供しています。プログラムは、このサービスを使うことでループなどしなくても必要なイベントの発生や終了のタイミングを合わせることができます。
上に挙げた例も、いつ発生するかわからない事柄です。インターバル・タイマーに関しては、例えば10秒後という時間はわかっていても、いつが10秒後なのかわかりません。人間は時計を見ながら10秒後を知ることができますが、これはループと同じです。オペレーター・コマンドや端末の受信データはわかりやすいでしょう。いつコマンドが入力されるか、いつ実行(ENTER)キーが押されるかは操作する人間次第です。I/O動作の完了やJCLサブミットの待ち合わせは、OSに取っての待ち合わせ事象の1つです。チャネルで実行されたI/Oはいつ完了するかわかりません。JCLは人間がコマンドやパネルでサブミット操作を行いますが、OSにしてみればそれがいつ行われるかまったく予測できません。
これらのようにいつ起きるかわからない事を非同期事象(asynchronous event)と呼びます。バッチ処理用のアプリケーション・プログラムを作る際は殆ど意識することはありませんが、MVSは基本的にイベント駆動型のソフトウェアです。様々な非同期事象の発生を待ち受け、それに応じた処理をディスパッチする、というサイクルを延々と繰り返して行きます。
タイマーによるインターバル時間の経過を待ち合わせる(WAIT)
1 2 3 4 5 6 7 8 9 |
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- MVI WKUPECB,0 CLEAR ECB STIMER REAL,TIMREXIT, SET TIMER + BINTVL=INTERVAL WAIT ECB=WKUPECB WAKE-UP AT 10SECOND LATER : : WKUPECB DC F'0' ECB FOR WAKE-UP MAINLINE INTERVAL DC F'1000' 10.00SEC(1/100SEC ORDER) |
WAITマクロは、イベントの完了を待ち合わせます。WAITマクロでは、どのイベントかを識別するためECBと呼ばれる1ワードの領域を使用します。ECBの先頭のビット0をWAITビット、ビット1をPOSTビットと呼びます。WAITマクロを発行するためには、ECBを用意してビット0をクリアーしておかなければなりません。サンプルでは、ビット0のクリアーではなくMVIでバイト毎クリアーしていますが、これも一般的な方法で非同期事象の開始前であることがはっきりしている時は、その時点ではこのECBにPOSTするプログラムは存在しないためPOSTビットも含めて0クリアーしてかまいません。XC命令でワード毎クリアーしてもかまいません。
イベントの識別と言っても、ややこしい規約があるわけではなく、複数のイベントを並行して取り扱うような場合、それぞれのイベントをどのECBを使って待ち合わせるかを取り決めます。一般的には、WAITする前にWAITに使うECBのアドレスを実際に非同期事象を実行する側のプログラム(POSTする側)に何らかの手段で伝えます。
WAITマクロを発行すると、プログラム(タスク)は指定したECBにPOSTがなされるまで、スーパー・バイザー(WAITルーチン)の中で待ち状態になります。ECBのWAITビットは1にされ、WAIT中プログラムを識別するトークン(RBアドレス)が下位3バイトに格納されます。
1 2 3 4 |
0 1 2 31 +---+---+--------------------//-------------------+ | W | P | POST CODE | +---+---+--------------------//-------------------+ |
ECBをどこでクリアーするかは重要です。待ち合わせる事象が開始する前にクリアーするのが原則です。タイマーならSTIMER発行前、オペレーター応答の受け取りならWTOR発行前、データの受信ならRECEIVE要求の前です。イベントを開始させてしまってからクリアーすると、タイミングによってはイベントをロストさせてしまいます。イベントによっては、WAITしようとする時点で既にPOSTされている場合もあり得ます。なお、既にPOST済みのECBを指定してWAITマクロを発行しても問題ありません。その場合、実際にはWAITせず直ちに発行元プログラムに戻ってきます。WAITビットが1になっているECB(WAIT中ECB)を指定すると、プログラムはABEND(2重WAIT)させられます。
1 2 3 4 5 6 |
【誤ったECBクリアーの例】 ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- STIMER REAL,TIMREXIT, SET TIMER + BINTVL=INTERVAL MVI WKUPECB,0 CLEAR ECB WAIT ECB=WKUPECB WAKE-UP AT 10SECOND LATER |
これは誤ったECBクリアーの例です。STIMETRマクロで指定する時刻や間隔時間にもよりますが、STIMERマクロから復帰した時点で既にWKUPECBにPOSTされているかも知れません。指定した間隔時間がn/100秒など極端に短い時間なら起き得ることです。サンプルでは無条件にECBをクリアーしていますから、その場合永遠にPOSTされないWAITに入ってしまいます。これがイベントのロストです。WAIT発行前にPOSTビットをテストして、1であればWAITを迂回するロジックもありですが、この例に関して言えばそんなことをするよりはECBのクリアーをSTIMETRマクロの発行前に行えばよいのです。非同期事象は、その名の通り非同期ですから指定したイベントがいつ完了するかは予測できませんが、この例では自分がSTIMERを出さない限り、時間計測と言う非同期イベントは起き得ないのでPOSTされることもありません。そのきっかけとなるSTIMERマクロの前でECBをクリアーできます。
タイマーによるインターバル時間の経過を通知する(POST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- MVI WKUPECB,0 CLEAR ECB STIMER REAL,TIMREXIT, SET TIMER + BINTVL=INTERVAL WAIT ECB=WKUPECB WAKE-UP AT 10SECOND LATER : : WKUPECB DC F'0' ECB FOR WAKE-UP MAINLINE INTERVAL DC F'1000' 10.00SEC(1/100SEC ORDER) : : TIMREXIT DS 0H USING *,R15 DEFINE TIMER EXIT BASE REG ST R14,12(,RD) SAVE RETURN ADDRESS L R1,AECB LOAD ECB ADDRESS POST (1) WAKE-UP MAINLINE ROUTINE L R14,12(,R13) LOAD RETURN ADDRESS B 0(,R14) RETURN TO OS AECB DC A(WKUPECB) PTR TO MAINLINE ECB WORD DROP R15 FORGET BASE REG POSTコードも設定する例 L R1,AECB LOAD ECB ADDRESS LA R0,4 LOAD POST CODE POST (1),(0) POST WITH COMPLETION CODE |
POSTマクロは、WAITの逆でイベントの完了を通知します。指定したECBにPOSTビットを設定し、WAIT中のプログラムがあれば待ち状態を解きます。この時、ECBのビット2~31に任意のPOSTコードを設定することもできます。省略すれば、ビット2~31は0にクリアーされます。POSTする側は、相手がWAITしているかどうかを考える必要はありません。
このサンプルは、STIMERマクロによるインターバル時間の経過の待ち合わせと通知の例です。STIMERマクロを出した後のWAITマクロによって、プログラムのタスクは待ち状態になります。10秒経過するとTIMREXITルーチンが実行されます。タスクが止まっている状態でも割り込んで実行されます。このようなプログラムを「非同期出口ルーチン」と呼びます。MVSでは、長時間掛かるサービスやいつ完了するか予測できないサービスの完了を通知するために非同期出口ルーチンが使われるものがいくつかあります。
WAITとPOSTはマルチタスクのプログラムで使うものというイメージがありますが、シングル・タスクのプログラムでも使用するAPIによっては、WAITとPOSTによる待ち合わせと通知の処理が必要になります。
複数のイベントを待ち合わせる(ECBLIST)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- WAIT ECBLIST=ECBLIST WAKE-UP AT POSTED BY ANY EVENT + COMPLETION : : ECBLIST DC A(ECB1) MULTIPLE ECBS LIST DC A(ECB2) DC A(ECB3) DC A(ECB4+X'80000000') ECB1 DC F'0' ECB FOR OPERATOR CMD INPUT ECB2 DC F'0' ECB FOR INTERVAL TIMER ECB3 DC F'0' ECB FOR TERMINAL RECEIVE ECB4 DC F'0' ... |
複数の非同期イベントを取り扱うプログラムでは、いずれかのイベントが完了したら通知してもらうようにすることもできます。上記サンプルは、複数のECBの内どれか1つが完了したら通知してもらうものです。これは、マルチイベント・シングルウェイトです。複数個のECBが完了したら通知してもらうようにすることも(マルチイベント・マルチウェイト)、全てのECBが完了したら通知してもらうようにすることも(マルチイベント・オールウェイト)できます。
ただし、マルチウェイトの場合はどのECBから完了したかはわかりません。シングルウェイトであっても、WAITが解けた後に別のECBにもPOSTされることがあり得ますので、やはりどのECBから終わったかはわかりません。どちらの場合であっても、どのECBのイベントが終わったかは、プログラムが自分でPOSTビットの立っているECBを探す必要があります。
なおマルチイベントでは、WAITから復帰した後のPOSTビットとPOSTコードは、POSTされたECBにのみ設定されています。POSTされたECBを含めて全てのECBのWAITビットは0になっていますが、POSTされなかったECBのPOSTコード部にはRBアドレスが設定されたままになっていることに注意してください。MVS側は、ECBにRBアドレスが設定されたままで再びWAITが出されても支障ありませんが、POSTされなかったECBはECBワード自体が0(あるいはWAIT発行時の内容)になっているものと期待してロジックを作ると正しく動きません。