【テクニカル・上級編】ON ENDFILE条件によるファイル読み込み終了の制御構造 – PL/Iの基本構文とデータ制御実践ガイド

汎用機の深淵:PL/IにおけるON ENDFILEと制御フローの「行間」を読む

メインフレームの心臓部で、何十年も止まることを許されずに稼働し続けるPL/Iプログラム。その保守やJava/C#へのマイグレーションを担当することになったエンジニアの諸君、ようこそ。

今日は、一見シンプルに見えて、実は「落とし穴」だらけの`ON ENDFILE`条件と、その背後にある制御フローの真実について話そう。単なる教科書的な構文解説ではない。基幹システムの現場で、ダンプを片手に夜を明かすことのないよう、コンパイラの「思考」に合わせた実装の極意を伝授する。

ON ENDFILE:制御が吸い込まれる「見えない落とし穴」

PL/Iにおいて`ENDFILE`条件は、単なるEOF検知ではない。コンパイラが生成する異常系への分岐ジャンプだ。

/i
/ 基本的なON ENDFILEの構造 /
ON ENDFILE(SYSIN) BEGIN;
PUT SKIP LIST(‘ファイル終端を検知しました。終了処理を開始します。’);
/ ここでフラグを立ててループを抜けるのが定石 /
EOF_FLAG = ‘1’B;
END;

READ FILE(SYSIN) INTO(REC_BUFFER);
DO WHILE(EOF_FLAG = ‘0’B);
/ 処理ロジック /
READ FILE(SYSIN) INTO(REC_BUFFER);
END;

ここで重要なのは、`ON`ユニットが実行された後、制御がどこに戻るかだ。`BEGIN`ブロック内で`GO TO`を使わずに`END`に達した場合、制御は「条件が発生したREAD文の直後」へ戻る。しかし、ループ構造の中でこれを使うと、意図しない再読み込みや無限ループを招くことがある。

アーキテクトの視点:
マイグレーション先がJava/C#の場合、この「非局所的な分岐」は例外処理(try-catch)で再現するわけだが、PL/I特有の「ONユニットの有効範囲(スタック上のスコープ)」を正しく理解していないと、移行先で条件分岐が暴走する。特に、CICS環境下での`SIGNAL ENDFILE`を用いた強制終了シミュレーションは、トランザクションのロールバック整合性と密接に関わるため、注意が必要だ。

ユニットテストの鬼:SIGNAL ENDFILEによるエッジケース検証

本番環境で滅多に発生しない「ファイルが空」という状況をテストするために、`SIGNAL ENDFILE`を使うのは定石だ。

/i
/ テストシミュレーション用コード /
IF TEST_MODE = ‘Y’ THEN DO;
SIGNAL ENDFILE(SYSIN); / 強制的にENDFILE条件を発生させる /
END;

しかし、ここで注意すべきは「パックデシマルの内部符号」だ。ファイル終端直前に、ゴミデータや不完全なレコードが混入している場合、`READ`した瞬間にデータ変換エラーが発生し、`ENDFILE`に到達する前に`CONVERSION`条件でアベンド(ABEND)する。
特に、ホストのパックデシマル(COMP-3)は、最後のニブルが`C`や`F`以外(例えば`E`など)だと、コンパイラは即座にS0C7のアベンドを叩き出す。これを回避するには、`ON CONVERSION`ユニットを適切に配置し、ダンプを解析する前に「データがそもそも壊れていないか」をチェックするルーチンが不可欠だ。

メモリ操作とポインタ:動的構造体の罠

大規模なバッチ処理で、入力データが可変長である場合、ベース付き変数(`BASED`変数)とポインタ操作を多用するだろう。

/i
DCL BUFFER_PTR PTR;
DCL 1 REC_STRUC BASED(BUFFER_PTR),
2 KEY_VAL CHAR(8),
2 DATA_VAL CHAR(100);

/ GET STORAGEによる動的メモリ確保 /
ALLOCATE REC_STRUC;

ここで、`ENDFILE`時に適切に`FREE`を行わないと、長時間稼働するバッチでは「メモリリーク」が発生し、最終的にOS側から強制終了させられる。JavaのGC(ガベージコレクション)に慣れた世代には理解しがたいかもしれないが、汎用機では「確保したものは自ら責任を持って解放する」のが鉄則だ。

コンパイラ最適化と「見えないバグ」

最後に、コンパイラオプションの罠について触れておく。`OPTIMIZE(3)`をかけると、コンパイラは「この変数はループ内で変化しない」と勝手に判断し、レジスタへ値をキャッシュしてしまう。
もし、他言語(例えばCOBOL等)で書き込まれた共有メモリ領域をPL/Iから参照している場合、この最適化によって「値が更新されているのに古い値を読み続ける」という悪夢のようなバグが発生する。

現場の知恵:
共有領域を参照する変数は、必ず`AUTOMATIC`ではなく、`STATIC`かつ`VOLATILE`(またはコンパイラ固有の修飾子)を意識して定義すること。マイグレーションにおいても、最適化による副作用を考慮したメモリバリアの実装が、移行後の安定稼働を左右する。

結びに代えて

PL/Iは、ハードウェアの挙動を直接的に制御できる、極めて強力な言語だ。だが、その力は諸刃の剣である。`ON ENDFILE`一つ取っても、それが単なるEOF検知ではなく、OSと対話するためのシグナルであることを忘れてはならない。

諸君が今扱っているそのコードは、何十年もの間、企業の基幹業務を支えてきた誇り高き遺産だ。それを新しい言語に移すとき、単に構文を変換するのではなく、そのコードが背負ってきた「ビジネスロジックの背景」と「ハードウェアの制約」を、設計図の中に正しく写し取ってほしい。

ダンプ解析に詰まったら、一度コードから離れ、コンパイラが生成する機械語の並びを想像してみることだ。そうすれば、自ずと答えは見えてくる。健闘を祈る。

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