メインフレームの「静寂」を操る:STATIC変数の真実と再入可能性の罠
若手からよく受ける相談に、「プログラムが2回目に呼ばれたとき、なぜか前の値が残っていてバグる」というものがある。原因の9割は`STATIC`ストレージクラスの特性に対する理解不足だ。
メインフレームの基幹バッチで、`STATIC`は強力な武器だが、同時に一歩間違えばシステムを道連れにする爆弾にもなる。今日は、PL/Iにおける`STATIC`の挙動と、現代の再入可能(REENTRANT)環境における制約について、現場の知見を交えて語ろう。
—
1. STATICストレージの「永続性」という性質
PL/Iにおいて`STATIC`属性を持つ変数は、プログラムの実行開始から終了まで、その領域が解放されることはない。`AUTOMATIC`(デフォルト)変数が`PROCEDURE`の呼び出しのたびにスタック上で生成・消滅を繰り返すのに対し、`STATIC`はロードモジュール内のデータセクションに固定的に配置される。
これが意味するのは「値の持ち越し」だ。バッチ処理で、前回のレコード読み込み時の累積値を保持しておきたい場合には非常に便利だが、サブルーチンを繰り返し呼び出す際に、初期化を忘れると悲劇が待っている。
2. コードで見る「STATIC」の生存戦略
VSAMファイルを順次読み込み、処理件数をカウントして終了時にログ出力するような、よくあるバッチ処理の断片を見てみよう。
1
/ プログラムの基本構造:PACKAGEとメインPROCEDURE /
TEST_PROG: PACKAGE;
/ STATIC変数はモジュールロード時に一度だけ初期化される /
DCL REC_COUNT FIXED BIN(31) STATIC INIT(0);
DCL EOF_FLAG BIT(1) STATIC INIT(‘0’B);
/ メイン処理部 /
MAIN_PROC: PROCEDURE OPTIONS(MAIN);
DCL VSAM_FILE FILE RECORD INPUT ENV(VSAM);
DCL VSAM_REC CHAR(100);
/ ファイルオープンとエラーハンドリング /
ON ENDFILE(VSAM_FILE) EOF_FLAG = ‘1’B;
OPEN FILE(VSAM_FILE);
DO WHILE(EOF_FLAG = ‘0’B);
READ FILE(VSAM_FILE) INTO(VSAM_REC);
IF EOF_FLAG = ‘0’B THEN DO;
REC_COUNT = REC_COUNT + 1; / 処理件数を累積 /
END;
END;
PUT SKIP LIST(‘TOTAL RECORDS PROCESSED: ‘ || TRIM(REC_COUNT));
CLOSE FILE(VSAM_FILE);
END MAIN_PROC;
END TEST_PROG;
このコードのポイントは、`REC_COUNT`が`STATIC`であるため、もしこのモジュールが何らかの理由で(例えば動的呼び出しで)複数回実行された場合、2回目以降は「0」から始まらない可能性があるということだ。実務では、再利用性を担保するために、終了時に必ず初期化するか、`AUTOMATIC`変数として渡す設計が望ましい。
—
3. REENTRANT(再入可能)プログラムという壁
いまどきのCICSアプリケーションや、マルチスレッドを意識した高負荷バッチでは、プログラムを`REENTRANT`にするのが鉄則だ。ここで`STATIC`を使うと、コンパイラから警告が出たり、最悪の場合は実行時に予期せぬ上書きが発生する。
なぜか?
`REENTRANT`プログラムは、複数のタスクが同時に同一コードを共有する。もしそこに書き込み可能な`STATIC`変数が存在すると、Aタスクが書き換えている最中にBタスクが値を読み込み、計算が崩壊する。いわゆる「競合状態(Race Condition)」だ。
- 鉄則1: `REENTRANT`属性をつけるなら、書き換え可能な`STATIC`変数は厳禁。
- 鉄則2: 定数(`STATIC INITIAL`)として読み取り専用にするならOK。
- 鉄則3: どうしても状態を保持したいなら、`GET STORAGE`で取得したヒープ領域(ポインタ経由)を、呼び出し元から引数として渡す設計にせよ。
—
4. ベテランからのトラブルシューティングのヒント
現場で「前回の値が残っている」というバグに直面したら、まずはコンパイルリストのMAPを確認してほしい。どの変数がどのストレージクラスに配置されているかが一目瞭然だ。
また、`ON-UNIT`(例外処理)の中で`STATIC`変数を更新する際は要注意だ。「エラーが起きたときだけカウントアップする」といったロジックを組む際、その変数が本当にそのタイミングで生存しているか、あるいは再帰呼び出しによって多重に更新されていないか、常に疑うこと。
最後に
PL/Iは古い言語だが、そのメモリ制御の緻密さは現代の高級言語にも引けを取らない。`STATIC`は、プログラムのライフサイクルをコントロールするための高度な機能だ。これを「ただのグローバル変数」として雑に扱うか、メモリ効率とロジックの永続性を両立させるための「計算された設計」として使うか。
君たちが書くそのコードが、10年後の後輩エンジニアに「見事な設計だ」と言われるような、堅牢なシステムの一部であることを期待している。
何か深掘りしたい技術テーマがあれば、いつでも聞いてくれ。メインフレームの深淵へ、いつでも案内しよう。
