Follow

How Transactions Are Managed: ETP (etrans) + the Heirloom PL/I Runtime

Table of Contents

  1. The two layers
  2. Execution strategies: instance vs. batch
  3. A transaction's lifecycle
  4. Nested calls: the execution stack
  5. Resource lifetime: three scopes
  6. What ETP provides to your program
  7. Observing the lifecycle
  8. Summary
  9. Related articles

Audience: developers and support engineers running CICS-style PL/I applications migrated to Java with Heirloom.


The two layers

A migrated online application runs on two cooperating layers:

  • ETP (the etrans transaction framework) — Heirloom's transaction engine. It plays the role CICS played on the mainframe: it receives a request, sets up the transaction environment (terminal, commarea, EIB, journaling, recovery), and drives your program by calling into it. It also orchestrates program-to-program calls (CICS LINK).
  • The PL/I Runtime — the library your migrated program is built on. Each online program extends a runtime base class and runs inside the transaction ETP sets up. The runtime manages the program's data, storage, SQL, and condition handling for the life of that transaction.

ETP owns the transaction boundary; the runtime owns the program state within it. This article explains how the two interact and what that means for resource lifetime.


Execution strategies: instance vs. batch

How a program manages its variables depends on the strategy it was migrated with:

Strategy Base class Variables Used for
Instance ETPBase instance fields CICS/online transactions, multi-threaded servers — each invocation gets fresh state
Batch / static ETPBatch static fields single-threaded batch jobs

Online transaction programs use the instance strategy (ETPBase), which is what the rest of this article describes. The instance strategy is what makes a program safe to run concurrently on many pooled threads: each transaction works on its own object state.


A transaction's lifecycle

For each program ETP runs, it makes two calls into the runtime base class:

  1. link(...) — ETP hands over the transaction environment and (optionally) a commarea. The runtime:
    • registers this program execution (see execution stack below),
    • wires up the EIB (Execute Interface Block — task/terminal/date/time info) and the transaction environment,
    • decodes the commarea into a structure and invokes your program logic,
    • copies any commarea changes back so the caller sees them.
  2. close() — ETP signals that this program execution is finished. The runtime reclaims that execution's resources.

A standalone (non-transactional) program simply runs without this wrapper; the lifecycle above applies to programs driven by ETP.


Nested calls: the execution stack

Real transactions are rarely a single program. One program LINKs to another, which links to another — a call chain. ETP calls link() for each, and the runtime tracks the chain on a per-thread execution stack:

PROGA           ← outermost (the transaction)
 └─ PROGB       ← linked
     └─ PROGC   ← linked

Each entry carries a unique execution id, the program name, the thread, and a start time. When a program starts, its frame is pushed; when it finishes, its frame is popped.

Important: ETP calls close() for every program in the chain — the nested ones and the outermost — not just at the very end. close() is not a one-time "transaction is over" signal. The runtime knows it has reached the true transaction boundary only when, after popping its own frame, the stack is empty.

This is why the runtime trusts the stack rather than program names or assumptions about ordering: it is the only reliable way to tell a nested completion from the end of the whole transaction.


Resource lifetime: three scopes

The runtime classifies the resources it manages into three lifetimes, and reclaims each at the right moment:

Scope Examples Reclaimed
Per-program (execution-scoped) SQL cursors/builders, the program's runtime instance & condition handlers when that program's close() runs
Cross-program (transaction-scoped) Pointers/tokens marshalled through commareas, task/GETMAIN storage only at the outermost close() (transaction end)
Application-scoped Global Work Areas (EXTRACT EXIT), GETMAIN SHARED never at transaction end — only by explicit FREEMAIN

Two principles make this correct and safe:

  1. A nested program only cleans up its own execution. Per-program resources are tagged with the execution id, so reclaiming them at a nested close() can never touch a still-running parent's state.
  2. Cross-program registries are swept only at the transaction boundary. A pointer passed from one program to a sibling or subsequent program must stay valid for the whole transaction. Reclaiming it when an intermediate program finishes would pull the rug out from under a later program — the classic cause of a "buffer too small" / TOOSMALL failure or a lost commarea. So those registries are cleared only when the outermost program completes, and the sweep preserves application-scoped Global Work Areas.

In short: inner completions reclaim only their own work; the transaction boundary reclaims the shared, transaction-lived work; Global Work Areas survive both. This is what keeps long-running, pooled server threads memory-stable across millions of transactions without ever discarding state a program still needs.


What ETP provides to your program

During link(), the runtime populates the standard execution context your PL/I program expects:

  • Commarea — the communication area passed between programs, decoded into a structure your code reads and updates; changes are copied back on return.
  • EIB (DFHEIBLK) — transaction id, terminal id, task number, date/time, AID, commarea length, etc., filled from the transaction environment.
  • Transaction services — program control, journaling, recovery, temporary storage, basic mapping support (BMS), and built-in functions, all supplied by ETP and surfaced through the runtime.

Your program reads and writes these exactly as it did under CICS; the runtime and ETP translate between the Java transaction environment and the PL/I view.


Observing the lifecycle

To watch transactions flow through the runtime, enable tracing (see JVM Options & Runtime Configuration):

java -Dheirloom.trace=true -jar my-application.jar

You'll see lines such as:

[TRACE] link ENTER nested 'PROGB' contextId=... depth=1 ...
[TRACE] close NESTED  'PROGB' ... — execId=... SQLBuilder/PLIRuntime
[TRACE] close OUTERMOST 'PROGA' ... + Pointer/Entry thread sweep
  • link ENTER ... depth=N shows the call chain forming.
  • close NESTED reclaims only that program's execution-scoped resources.
  • close OUTERMOST is the transaction boundary — it additionally sweeps the cross-program registries (while preserving Global Work Areas).

A healthy trace shows per-thread resource counts returning to their baseline after each close OUTERMOST, with no upward drift across transactions.


Summary

  • ETP (etrans) owns the transaction; the PL/I Runtime owns program state inside it.
  • Online programs use the instance strategy (ETPBase) so they run safely on concurrent, pooled threads.
  • ETP drives each program via link() (set up + run) and close() (reclaim) — and calls close() for every program in a LINK chain.
  • The runtime tracks the chain on a per-thread execution stack and reclaims resources by scope: per-program at each close, cross-program at the outermost close, and Global Work Areas never (until explicit FREEMAIN).

  • How the PL/I Runtime Manages Memory — structures, pointers, GWA, BASED/DEFINED.
  • JVM Options & Runtime Configuration — enabling the lifecycle trace shown above.
Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request

0 Comments

Please sign in to leave a comment.
Powered by Zendesk