【実務・中級編】STATICストレージクラスの初期化タイミングとモジュール間共有 – PL/Iの基本構文とデータ制御実践ガイド

静的変数の深淵:PL/IにおけるSTATICとEXTERNALの「定石」を極める

メインフレームの現場でPL/Iコードを触っていると、ふとした瞬間に「なぜこの変数はこのタイミングで値を持っているのか?」という壁にぶつかることがある。特に、バッチ処理の複雑な階層構造の中で、`STATIC`属性と`EXTERNAL`属性が絡み合うとき、その挙動を正しく理解していないと、デバッグ地獄への片道切符を手にすることになる。

今日は、若手のエンジニアがつい曖昧にしがちな、PL/Iのメモリ管理の根幹について語ろうと思う。

1. STATICストレージの「ロード時」という厳粛な事実

PL/Iにおいて`STATIC`を指定した変数は、プログラムが呼び出されるたびに初期化されるのではなく、プログラムがロードされた(メモリに展開された)その瞬間に一度だけ初期化される。

ここが重要だ。例えば、バッチジョブの中で複数のエントリーポイントを持つモジュールや、再帰的に呼び出されるルーチンにおいて、「前回処理した時の値が残っている」という挙動は、多くの場合`STATIC`の仕業だ。

実践的なコード例:初期化の罠を避ける

1
TEST_MOD: PROCEDURE OPTIONS(MAIN);

/ STATIC変数はロード時に一度だけ初期化される /
DCL MSG_COUNT FIXED BIN(15) STATIC INIT(0);

/ 処理ルーチン /
CALL INCREMENT_COUNTER;
CALL INCREMENT_COUNTER;

PUT SKIP LIST(‘現在のカウント値:’ || MSG_COUNT);
/ 結果は 2 となる。これがSTATICの特性だ /

INCREMENT_COUNTER: PROCEDURE;
MSG_COUNT = MSG_COUNT + 1;
END INCREMENT_COUNTER;

END TEST_MOD;

この「値が残る」という特性は、ログの集計や、制御フラグの保持には非常に便利だが、初期化漏れによるバグの温床にもなる。もし「呼び出すたびにリセットしたい」のであれば、`STATIC`ではなく`AUTOMATIC`(デフォルト)を使うのが鉄則だ。

2. EXTERNALによるモジュール間共有の「諸刃の剣」

大規模な基幹システムでは、共通の制御テーブルやフラグを複数のプログラムから参照したい場面がある。ここで登場するのが`EXTERNAL`属性だ。

`EXTERNAL`は、異なるコンパイル単位(モジュール)間で、同じ名前の領域を物理的に共有させる仕組みだ。しかし、ここで気をつけなければならないのが「宣言の一貫性」である。

モジュール間共有の正しい作法

例えば、共通のデータ領域を定義する場合、以下のようなコピーブック(INCLUDEメンバ)を介して定義を統一するのが、メインフレーム開発の「美学」であり、安全策だ。

1
/ 共通定義部: COMMON_DATA.PLI /
DCL 1 SHARED_AREA EXTERNAL,
2 PROCESS_ID CHAR(4), / 処理ID /
2 RET_CODE FIXED BIN(31); / 終了コード /

各プログラムではこれを`%INCLUDE`する。もし、片方のモジュールで`RET_CODE`を`FIXED BIN(15)`と定義し、もう一方で`FIXED BIN(31)`と定義してしまったらどうなるか? 実行時にメモリの境界がずれて、全く関係のない変数の値が書き換わるという、追跡困難な怪奇現象が発生する。

3. レコード入出力とONユニットの制御

`STATIC`や`EXTERNAL`で定義した制御変数を使ってVSAMファイルへアクセスする場合、`ONCODE`や`ONFILE`を用いた`ON`ユニットでの制御が不可欠だ。

現場でよく見るのは、入出力エラーをハンドリングする際に、グローバルなエラーフラグ(`EXTERNAL`)を立てて、後続の処理を打ち切るパターンだ。

1
ON ENDFILE(VSAM_FILE) BEGIN;
/ 終了判定フラグを立てる /
EOF_FLAG = ‘1’B;
END;

/ ファイル読み込みループ /
DO WHILE(EOF_FLAG = ‘0’B);
READ FILE(VSAM_FILE) INTO(REC_BUF);
IF ONCODE > 0 THEN DO;
/ エラーハンドリング:ログ出力して即座に終了 /
CALL LOG_ERROR(ONCODE);
SIGNAL FINISH;
END;
END;

ここでのポイントは、`ON`ユニット内ではあまり重い処理をさせないことだ。`STATIC`な変数の更新や、最低限のフラグ制御に留める。複雑なロジックを`ON`ユニットに詰め込むと、スタックオーバーフローや予期せぬ制御フローの乱れを引き起こす可能性がある。

ベテランからのアドバイス:保守の現場で生き残るために

PL/Iのコードを保守する際、私がいつも心掛けているのは「変数のライフサイクルを視覚化すること」だ。

  • `STATIC`を使うときは、その変数がプログラム全体で「状態」を保持すべきものなのかを自問する。
  • `EXTERNAL`を使うときは、影響範囲がどこまで及ぶか(どのプログラムが書き込む可能性があるか)を必ず設計書で突き止める。
  • どんなに急いでいても、データ構造の定義は`%INCLUDE`で一元管理する。

PL/Iは古い言語だと言われることもあるが、メモリの配置をここまで緻密に制御できる言語は他にない。この特性を「敵」ではなく「味方」にできたとき、君たちはどんな複雑なバッチ改修も恐れることはなくなるはずだ。

何か具体的に「このモジュールの共有変数が怪しい」といったケースがあれば、いつでも相談してくれ。バグの臭いを嗅ぎ分けるのが、俺たちの仕事なんだからな。

タイトルとURLをコピーしました