03.1 資源の逐次化(排他制御)を行う(ENQとDEQ)

MVSではあらゆるデータを資源(Resource)として扱うことができます。CPU、1つのDASDボリューム、1つのデータセット、データセット内の1つのレコード、実行するプログラム・モジュール、仮想記憶上の領域などプログラムがアクセスできる装置やデータはすべて資源になります。MVSでは同時に複数のプログラムが動くので、同じ資源を扱うプログラムがその内容を同時に書き換えようとすることが起き得ます。これが資源の競合です。MVSでは競合を解決する手段として、資源を順番に使用させる「逐次化(Serialization)」という制御を行い、プログラムには逐次化を行うためのENQおよびDEQと呼ばれるサービスを提供しています。
ENQ/DEQでは競合する資源には名前を付けて管理します。名前はQ名およびR名で構成されます。一般にQ名が資源(データ)の分類名でR名が資源自身の名前となり、プログラムで自由に付けることができます。ただし、Q名はMVS自身が制御で使用する名前と重複すると混乱するので、SYSで始まる名前を使うべきではありません。MVSは、Q名とR名を実際の資源と関連付けることはしません。存在しない資源の名前を付けることも自由です。ただし、同じ資源を使うプログラムはすべて同じQ名とR名を使う必要があります。資源には、スコープと呼ばれる有効範囲があります。MVSは、Q名、R名及びスコープの3つが同じであれば同一資源とみなし、FIFOでその使用を1つのタスクにのみ許可します。ENQは資源の使用許可願いで、DEQは資源の使用終了連絡です。

ENQ/DEQの基本的な使い方

アプリケーションっぽいENQとDEQの例です。マスターファイル内の口座レコードの残高を更新します。マスターファイルから更新対象レコードを読み込み、残高フィールドを更新して書き戻します。レコードの読み込みから書き戻しまでの間、同じレコードが他のプログラムでアクセスされないように排他制御を掛けます。レコード内容の整合性を保つため、レコードの読み込み前に排他制御を掛けています。

ENQで排他制御を掛け、DEQでそれを解放します。Q名は8バイト固定で内容は任意です。R名は1~255バイトで内容は任意です。スコープにはOS内全体(複数のジョブ間で排他制御を行う)を指定しています。ENQマクロに指定したRET=HAVEは、資源が獲れなければ獲れるまで待ち状態に入ることを示します。ENQ/DEQにRETオプションが指定されていると、ENQ/DEQに失敗した場合は復帰コードで通知されます。GR15が0ならば要求は成功です。失敗した場合、GR15にはENQ/DEQのパラメーター・リストのアドレスが格納されます。復帰コードは、パラメーター・リスト内に設定されます。復帰コードそのものが格納されるわけではないことに注意して下さい。

ENQ/DEQの特徴は、ロックする資源は実体を伴わない点にあります。Q名とR名はプログラム間で取り決めたものであって、現実に対応する装置、ボリューム、データセットなどの実体の名前との関連付けなどは行われません。また、Q名とR名で指定された資源に対し、物理的に参照や更新などのアクセス自体をできなくすることもありません。あくまでもプログラム間で「○○する時はQ名とR名が書かれた札を取ってから行いましょう、札が使われていたら空くまで待ちましょう。」というルールを取り決めているだけです。ENQ/DEQはそのルールで使う札のしくみを提供しているようなものです。Q名とR名の札は単線の鉄道で使われる通票と同じで、それを持っていないと列車を走らせることができません。
関係するプログラム全てが同じルールを守ることが前提の排他制御なので、掟破りをする者がいれば成り立ちません。ENQを無視してデータセットに書き込みをすればレコードは壊れてしまいます。トランザクションのキューイングやカウンターの更新であれば、ロストしてしまうかも知れません。MVSは排他制御の仕組みを提供するだけで、正しい排他制御がなされるかはあくまでもアプリケーションの設計と運用に掛かってきます。
現在はデータベース・システムを使うので、サンプルで示したようなマスターファイルのレコード更新などはデータベース側の排他制御機能を利用することになります。しかし、排他制御が必要な資源はデータベースに格納されるデータ・レコードだけではありません。ENQ/DEQによる資源の逐次化制御は、MVSにおける排他制御の基本ですから必ず理解しておきましょう。

可変長のR名

このサンプルでは、R名の長さをENQマクロのパラメーターではなくR名領域の先頭1バイトに設定することで指定しています。この場合、ENQマクロではR名長を0としておきます。この指定方法を使えば、可変長のR名長に対応できます。なお、R名先頭のレングス・フィールドはR名には含まれません。
RET=TESTは、指定した資源が誰かに使用されているかをテストする目的で使います。誰も資源を使用していない場合も、実際にENQされることはありません。スコープにはシステム全体(複数のOS間で排他制御を行える)を指定しています。SYSTEMS ENQでは、各々のシステムのENQ要求はGRSによって変換・管理され、各々のシステムのGRSは互いのENQ要求をCTCやXCFを介して調整し合います。
サンプルで示したQ名とR名は、ISPFエディターが編集中のPDSメンバーをロックするためのものです。復帰コードが0でなければ、そのメンバーは誰かがISPFエディターで編集中ということになります。

複数資源の同時排他制御

複数のリソースを同時にENQすることもできます。サンプルのように、Q名、R名、タイプ、R名長、スコープの対を順番に指定します。RETパラメーターを省略しているので、指定した全ての資源の排他制御が獲れるまでプログラムには戻ってきません。2重ENQなどのプログラムミスがあった場合はABENDさせられます。2重ENQは、運用上は問題がなくてもプログラムに誤りがあることには変わりません。ABENDを嫌ってRET=HAVEやRET=USEにすることは多いのですが、その場合は復帰コード8で通知されます。復帰コードを判定してエラー処理をしなければ、仮に2重ENQがあっても潜在化されてしまいます。

アプリケーションなどで、1つの処理を行うために複数の資源を持つことはあり得ることです。この場合、資源を獲得する順番はとても重要です。AとBの2つの資源がある時、PGM1はA→Bの順番で資源を確保し、PGM2はB→Aの順番で資源を確保しようとします。タイミングによっては、PGM1がA、PGM2がBの資源を持った状態でお互いが自分が持っていない資源を獲りに行きます。しかしながら、その資源は既に相手が持ってしまっていますから獲ることができません。このような状態が「デッドロック」です。デッドロックを防ぐ方法として、必要な資源を確保する順番を決めるというのがあります。PGM2もA→Bの順番で資源を確保すれば、資源Aの確保の時点で実行が止まり、資源BはENQされないのでデッドロックになりません。もう1つの方法は、複数の資源を順番にではなく1度に全部確保することです。ENQ/DEQマクロで複数のリソースが指定できるのは、後者の方法をサポートするためです。

データセットの排他制御

RETパラメーターにUSEを指定すると、指定した資源が他のタスクやジョブで使用されている場合、ENQサービス内で待ちになることなく直ちにプログラムに制御が戻ります。復帰コードが4であれば、資源の排他制御は確保されていません。リソースが空いたかどうかは、自分で確認のロジックを作成する必要があります。また、ENQ資源はタスク単位に管理されます。ENQとDEQは同じタスクで発行しなければなりません。

このサンプルは、JCLのDDステートメントに定義されたデータセットと同じENQを発行しています。イニシエーターは、DDステートメントに定義されたデータセットを排他制御するためにENQを使います。Q名=SYSDSN、R名=データセット名、R名長=実際のデータセット名の長さ、SCOPE=SYSTEMです。
DISP=SHRの場合は共用制御のS、SHRでなければ排他制御のEが指定されます。サンプルでは、指定したDSNでJCLのDDステートメントと同じENQを発行します。資源が獲得できたら20秒間WAITします。20秒後に起き上がり、獲得した資源を解放します。このサンプルを実際に実行した場合、実行後20秒以内に下記のようなDDステートメントを持つJCLをサブミットすれば、「JOB ???????? WAITING FOR DATA SETS」のメッセージを出してデータセットが使用可能になるまで実行開始が延期されるはずです。

SYSで始まるQ名の内、MVSが制御のために使用するものは一般のプログラムでは使用できません。使用するためには最低でもAPF許可を受けていなければなりません。APF許可されていないプログラムでは、ENQマクロ発行時にABENDS338してしまいます。MVSの各コンポーネントがどのようなQ名、R名でENQを行っているかはマニュアル「MVS診断:解説書」にも載っています。
APF許可を受けたプログラムであれば、直ぐに資源が利用できない時、資源が使用できるようになったらECBに通知してもらったり、異なるタスクの資源をDEQしたりなどの制御プログラム用の機能を利用することができます。