現場で泣かないためのPL/I再帰処理:RECURSIVE属性とスタックの深淵
若手エンジニアから「バッチ処理でツリー構造のデータを走査したいのですが、PL/Iで再帰ってどう書くんですか?」と聞かれることが増えた。
PL/IはCOBOLのような手続き型言語の枠を超え、ポインタ操作やリスト処理までこなせる強力な言語だ。しかし、その柔軟さゆえに「再帰呼び出し(RECURSIVE)」の扱いを誤ると、夜間バッチで突如として発生する「S0C4」や「STORAGE ABEND」の悪夢にうなされることになる。
今日は、メインフレームの現場で生き抜くための、PL/I再帰処理の作法について深掘りしていこう。
—
1. RECURSIVE属性の正しい構文
PL/Iで再帰を実現するのは驚くほど簡単だ。`PROCEDURE`文に `RECURSIVE` 属性を付与するだけ。これだけで、当該手続き自身を自分の中から呼び出すことが可能になる。
/i
/ 再帰処理を行うための標準的なPROCEDURE定義 /
CALC_HIERARCHY: PROC(P_ID) RECURSIVE;
DCL P_ID FIXED BIN(31);
/ … ここにロジックを記述 … /
END CALC_HIERARCHY;
ここで重要なのは、「自動変数(DCLで定義したローカル変数)」は呼び出しのたびにスタック上に新規作成されるという点だ。これが再帰の便利さの源泉だが、同時に危険の火種でもある。
—
2. スタックオーバーフローを防ぐストレージ管理の極意
メインフレームのバッチジョブには、JCLの `REGION` サイズという明確な壁がある。再帰の深さが想定を超えると、スタック領域が枯渇し、システムは無慈悲に異常終了する。
実務で私が意識している「再帰の安全装置」は以下の3点だ。
- 深さの制限を設ける:
無限ループを防ぐため、再帰の深さをカウントするカウンタ変数を渡し、一定値を超えたらエラーログを出して終了させる設計にする。
- 自動変数のサイズを抑える:
再帰手続き内で巨大な配列(`DCL DATA_ARRAY(10000) CHAR(100);` など)を宣言してはいけない。スタックを食いつぶす。巨大なデータはポインタを介して制御領域(STORAGE)に確保する癖をつけよう。
- 不要なONユニットを避ける:
再帰呼び出しのたびに `ON CONDITION` などを多用すると、ハンドラの管理コストでスタックが圧迫される。基本は戻り値による正常終了チェックだ。
—
3. 実践コード:VSAM階層構造の走査例
実務でよくある「親IDをキーにして、子IDを次々とVSAMから読み込む」処理を想定したサンプルだ。
/i
PROC_TREE: PROC(P_ROOT_ID) OPTIONS(MAIN);
DCL P_ROOT_ID FIXED BIN(31);
/ 再帰手続きの呼び出し /
CALL SEARCH_CHILDREN(P_ROOT_ID);
/ 再帰手続き本体 /
SEARCH_CHILDREN: PROC(P_PARENT_ID) RECURSIVE;
DCL P_PARENT_ID FIXED BIN(31);
DCL L_CHILD_ID FIXED BIN(31);
DCL EOF_FLAG BIT(1) INIT(‘0’B);
/ VSAMアクセス用:キー指定して読み込み /
/ ※実際にはREAD/GETなどの制御がここに入る /
DO WHILE(^EOF_FLAG);
/ 子レコードの取得ロジック(省略) /
IF FOUND_CHILD THEN DO;
/ 再帰呼び出し:自身を呼ぶことで深い階層へ潜る /
CALL SEARCH_CHILDREN(L_CHILD_ID);
END;
ELSE EOF_FLAG = ‘1’B;
END;
END SEARCH_CHILDREN;
END PROC_TREE;
—
4. ベテランからのアドバイス:デバッグのコツ
もし本番環境で「再帰が原因と思われるABEND」が発生したら、まずは `PLIDUMP` を活用してくれ。
`CALL PLIDUMP(‘TFB’);` を再帰処理の分岐点に忍ばせておき、異常終了時にスタックトレースを吐かせるんだ。トレースを見れば、「どの深さで、どの変数がスタックを圧迫したのか」が一目瞭然だ。
また、マイグレーション等で古いコードを調査する際は、`OPTIONS(REENTRANT)` が指定されているかも確認すること。再帰を使うなら、マルチスレッド環境やリエントラント性が求められるモジュール設計との整合性も考慮が必要だ。
最後に
PL/Iの再帰は強力なツールだが、それは「制御できる」という確信があって初めて使えるものだ。スタックサイズとアルゴリズムの深さを常に天秤にかけ、必要であれば「再帰を使わないスタック実装(スタックを自分で管理するキュー方式)」への切り替えも検討してほしい。
基幹システムを支えるのは、こうした細かい積み重ねと、泥臭いまでのメモリ管理へのこだわりだ。君のコードが、次の夜間バッチを無事に乗り切ることを祈っている。

コメント