MODULE 11 · SUBPROGRAMS AND COPYBOOKS · 6/8

Static and dynamic CALL, CANCEL and ON EXCEPTION

16 min30 XPExercise

How does the called program actually get into memory? There are two answers, and which one your shop uses affects how changes are deployed.

Static calls

With a static call, the subprogram's object code is link-edited into the caller's load module. One load module contains both. The call is fast and can't fail at run time, but if the subprogram changes, every program that includes it must be link-edited again to pick up the change.

On IBM Enterprise COBOL, CALL "LITERAL" is static when the caller is compiled with the NODYNAM option.

Dynamic calls

With a dynamic call, the subprogram is its own load module. The first CALL finds it in the load libraries and loads it; later calls reuse the loaded copy. Replace the module in the library and the next run picks up the new version without touching any caller. Most large shops call their shared routines dynamically for exactly that reason.

A call is dynamic when:

  • the caller is compiled with DYNAM and uses CALL "LITERAL", or
  • the program name is in a data item: CALL WS-PGM. This is always dynamic, because the name isn't known until run time.

CALL identifier lets a program pick the routine from its data: one rating module per insurance product, one validation module per country. GnuCOBOL, which runs your code here, loads separately compiled subprograms dynamically for both forms.

CANCEL

CANCEL "NAME" (or CANCEL WS-PGM) releases a dynamically called program. The next CALL loads a fresh copy, so its WORKING-STORAGE is back to its VALUE clauses. You can list several names in one CANCEL. Cancelling a program that was never called does no harm.

ON EXCEPTION

If a dynamic CALL can't find the module, the run unit fails. GnuCOBOL prints libcob: error: module 'NOSUCH' not found; on z/OS the step abends with S806. When the program name comes from data, that is a real possibility, so trap it:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DYNDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-PGM           PIC X(8).
       PROCEDURE DIVISION.
           MOVE "COUNTER" TO WS-PGM
           CALL WS-PGM
           CALL WS-PGM
           CANCEL WS-PGM
           CALL WS-PGM
           MOVE "NOSUCH" TO WS-PGM
           CALL WS-PGM
               ON EXCEPTION
                   DISPLAY "CANNOT LOAD " WS-PGM
           END-CALL
           DISPLAY "STILL RUNNING"
           STOP RUN.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. COUNTER.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-N             PIC 9(3) VALUE 0.
       PROCEDURE DIVISION.
           ADD 1 TO WS-N
           DISPLAY "COUNTER CALL " WS-N
           GOBACK.
       END PROGRAM COUNTER.
       END PROGRAM DYNDEMO.
COUNTER CALL 001
COUNTER CALL 002
COUNTER CALL 001
CANNOT LOAD NOSUCH
STILL RUNNING

NOT ON EXCEPTION runs when the call did work. Close the statement with END-CALL.

A trap: DISPLAY has ON EXCEPTION too

DISPLAY accepts its own ON EXCEPTION and NOT ON EXCEPTION phrases. Write this:

           CALL "NOSUCH"
               ON EXCEPTION
                   DISPLAY "MISSING"
               NOT ON EXCEPTION
                   DISPLAY "LOADED"
           END-CALL

and the compiler attaches NOT ON EXCEPTION to the DISPLAY, not the CALL. The program prints MISSING and LOADED. Put the work in paragraphs (PERFORM 3000-NO-MODULE), or close the DISPLAY with END-DISPLAY.

On the job

An S806 in the job log means "module not found": usually a program promoted without its subprogram, or a STEPLIB/JOBLIB that points at the wrong load library. It's one of the first abend codes you will learn to recognise.

Your task

An insurer prices quotes in batch. Each product has its own rating subprogram, and the name of the module is the product code in the request. Today RATEHOME and RATEMOTR exist; any other product has no module yet. Both share one interface:

       LINKAGE SECTION.
       01  LS-SUM           PIC 9(7)V99.
       01  LS-PREMIUM       PIC 9(7)V99.
       01  LS-QUOTE-NO      PIC 9(3).
       PROCEDURE DIVISION USING LS-SUM LS-PREMIUM LS-QUOTE-NO.

Each module numbers its quotes from 001 in its own WORKING-STORAGE.

Complete QUOTEBAT. For each request line (8-character product code, then 9 digits of sum insured):

  • NEWDAY: display --- NEW DAY --- and CANCEL both rating modules so their quote numbers start again at 001.
  • otherwise CALL the module named in WS-PRODUCT with the sum, premium and quote number. If it loads, PERFORM 2000-SHOW-QUOTE. If it can't be loaded, count it in WS-REJECTED and display the product code followed by NO RATING MODULE.

The input

06
RATEHOME030000000
RATEMOTR001500000
RATEHOME045000000
RATEPETS000080000
NEWDAY
RATEHOME020000000

produces

RATEHOME Q001 PREMIUM     900.00
RATEMOTR Q001 PREMIUM     550.00
RATEHOME Q002 PREMIUM    1350.00
RATEPETS NO RATING MODULE
--- NEW DAY ---
RATEHOME Q001 PREMIUM     600.00
QUOTED 004 REJECTED 001

Check your understanding

1. On z/OS, which kind of CALL is always dynamic, whatever the compiler options?
2. After CANCEL "RATEHOME", the next CALL "RATEHOME"...
3. What happens when a CALL without ON EXCEPTION can't find the program?
fixed format
Run your program to see its output here. The first visible test's input and datasets are used.
Submit to grade your program against every test.