Using subprograms - IBM Mainframe

Sometimes an application is simple enough to be coded as a single, self-sufficient program. In many cases, though, an application's solution will consist of several programs bound together. A run unit includes one or more object programs, and may include object programs from languages other than COBOL. The first COBOL program to be executed in the run unit is usually the main program. When a run unit consists of several, separately compiled programs that call each other, the programs must be able to communicate with each other. They need to transfer control and usually need to have access to common data. The following sections describe the methods that accomplish this.

Another method that can be used for inter-program communication is the nesting of COBOL programs. This allows all the required subprograms for an application to be contained within the main program and thereby require only a single compilation.

Transferring Control to Another Program

In the Procedure Division, a main program can call a subprogram, and a subprogram may itself call yet another subprogram. The program that calls another program is referred to as the calling program, and the program it calls is referred to as the called program.

The called COBOL program starts executing at the top of the Procedure Division. (It is possible to specify another entry point where execution begins, using the ENTRY label in the called program. However, this is not a recommended practice in a structured program.) When the called program processing is completed, the program can either transfer control back to the calling program or end the run unit.

A called program must not directly or indirectly execute its caller (such as program X calling program Y; program Y calling program Z; and program Z then calling program X). This is called a recursive call. If you attempt to execute a recursive call to a COBOL program, the run unit will terminate abnormally (abend).

In a non-ClCS environment, the calling program and all called programs must all be compiled with either the RESIDENT or the NORESIDENT compiler option, unless the MIXRES run-time option has been specified.

Main Programs and Subprograms

No specific source code statements or options identify a COBOL program to be a main program or a subprogram. Normally, the first COBOL program to begin executing in a run unit becomes the run unit's main program. All other COBOL programs in the run unit are subprograms. Whether a COBOL program is a main program or a subprogram can be significant for either of two reasons:

  • If execution ends in the main program, the main program must use a STOP RUN or GOBACK statement. STOP RUN terminates the run unit and deletes all dynamically called programs in the run unit and all programs link-edited with them. (It does not delete the main program.) Control is returned to the caller of the main program, which is often the operating system. GOBACK has the same effect in the main program. An EXIT PROGRAM executed in a main program has no effect.

A subprogram may end with an EXIT PROGRAM, a GOBACK, or a STOP RUN statement. If the subprogram ends with an EXIT PROGRAM or a GOBACK statement, control returns to its immediate caller without ending the run unit. An implicit EXIT PROGRAM statement is generated if there is no next executable statement in a called program. If the subprogram ends with a STOP RUN statement, the effect is the same as it is in a main program—all COBOL programs in the run unit are terminated, and control returns to the caller of the main program. Unpredictable results will occur if a VS COBOL II program has been called by a non-COBOL program and performs a STOP RUN while running under COBTEST. The storage used for the run unit fields will be deleted by the non-COBOL program.

  • A subprogram is usually left in its last-used state when it terminates with EXIT PROGRAM or GOBACK. The next time it is called in the run unit, its internal values will be as they were left, except that return values for PERFORM statements will be reset to their initial values. In contrast, a main program is initialized each time it is called. There are two exceptions:
  • A subprogram that is dynamically called by an OS/VS COBOL or VS COBOL II program and then canceled, with the RESIDENT option in effect, will be in the initial state the next time it is called.
  • A program with the INITIAL attribute will be in the initial state each time it is called.

Passing Return Codes (RETURN-CODE Special Register)

You can use the RETURN-CODE special register to pass return codes from called programs to calling programs. We will see how this is implemented for separately compiled programs and nested programs. For separately compiled programs, the RETURN-CODE special register is used to pass return codes from a subprogram to a main program and from a main program to the system. When a COBOL program returns to its caller, the contents of its RETURN-CODE special register are stored into register 15. When control is returned to a COBOL program from a call, the contents of register 15 are stored into the calling program's RETURN-CODE special register. When a COBOL program returns control to the operating system, the RETURN-CODE special register contents are returned as a user return code.

For nested programs, the RETURN-CODJ special register is treated like a numeric data item declared with the GLOBAL attribute in the outermost program. Unlike separately compiled programs, when nested programs are invoked, the RETURN-CODE special register is not initialized to zero.

Calling the Programs

You may use several different methods to transfer control to another program. These include:

  • Calls to nested programs
  • Static calls to other separately compiled VS COBOL II programs
  • Dynamic calls to other separately compiled VS COBOL II programs

In a non-ClCS environment, calls to programs in other languages, including PL/I, FORTRAN, and OS/VS COBOL.Calls to nested programs allow you to create applications using structured programming techniques. They can also be used instead of PERFORM procedures to prevent unintentional modification of data items. Calls to nested programs can be made using either the CALL literal or CALL identifier statement.

A static call is used to invoke a separately compiled program that is link-edited into the same load module as the calling program. A dynamic call is used to invoke a separately compiled program that has been link-edited into a separate load module from the calling program. In this case, the subprogram module is loaded into storage the first time it is called.

In a non-CICS environment, COBOL programs and non-COBOL programs can call and be called by PL/I, FORTRAN, and assembler language programs.

Nested Programs

Nested programs give you a method to create modular functions, for your application and maintain structured programming techniques. They can be used as PERFORM procedures with the additional ability to protect 'local' data items.

Nested programs allow for debugging a program before including it in the application. You can also compile your application with a single invocation of the compiler. A COBOL program may contain other COBOL programs. The contained programs may themselves contain yet other programs. A contained program may be directly or indirectly contained within a program.

There are several conventions that apply when using nested program structures:

  • The Identification Division is required in each program. All other divisions are optional.
  • Program names must be unique.
  • Contained program names may be any valid COBOL word or a nonnumeric literal.
  • Contained programs cannot have a Configuration Section. The outermost program must specify any Configuration Section options that may be required.
  • Each contained program is included in the containing program immediately before its End Program header.
  • Contained and containing programs must be terminated by an End Program header

A contained program may only be called by its directly containing program, unless the contained program is identified as COMMON in its Program-Id clause. In that case, the COMMON program may also be called by any program that is contained (directly or indirectly) within the same program as the COMMON program. Only contained programs can be COMMON. Recursive calls are not allowed.

The following figure shows the outline of a nested structure with some contained programs identified as COMMON (programs A12, A2 and A3—marked black on the side and letters in bold).

The following table describes the 'calling hierarchy' for the structure that is shown in the following figure. Notice that programs A12, A2, and A3 are identified as COMMON and the resulting differences in calls associated with them. Note that:

  • A2 cannot call A1 because A1 is not common and is not contained in A2.
  • A111 cannot call A11 because that would be a recursive call.
  • Al can call A2 because A2 is common.
  • Al can call A3 because A3 is common.

A Nested Structure with COMMON Programs

A Nested Structure with COMMON Programs

There are two classes of names within nested structures—local and global. The class will determine whether a name is known beyond the scope of the program that declares it. There is also a specific search sequence for locating the declaration of a name after it is referenced within a program.

  • Local Names - Names are local unless declared to be otherwise (except the program name). These local names are not visible or accessible to any program outside of the one where they were declared; this includes both contained and containing programs.
  • Global Names - A name that is specified as global (by using the Global clause) is visible and accessible to the program in which it is declared, and to all the programs that are directly and indirectly contained within that program. This allows the contained programs to share common data and files from the containing program, simply by referencing.fhe name of the item.

Any item that is subordinate to a global item (including condition names and indexes) is automatically global. The same name may be declared with the Global clause multiple times, provided that each declaration occurs in a different program. Be aware that masking, or hiding, a name within a nested structure is possible by having the same name occur within different programs of the same containing structure. This could possibly cause some problems when a search for a name declaration is taking place.

When a name is referenced within a program, a search is made to locate the declaration for that name. The search begins within the program that contains the reference and continues 'outward' to containing programs until a match is found. The search follows this process:

    • Declarations within the4program are searched first.
    • If no match is found, then only global declarations are searched in successive outer containing programs.
    • The search ends when the first matching name is found; otherwise, an error exists if no match is found.

You should note that the search is for a global 'name,' not for a particular type of object associated with the name, such as a data item or file connector. The search stops when any match is found, regardless of the type of object. If the object declared is of a different type than what was expected, an error condition exists.

Static and Dynamic Calls

The following discussion applies to separately compiled subprograms only, not to nested (contained) programs. When a subprogram is called, it may already be in main storage having been link-edited in the same load module with the calling program (static call). Or it may be loaded only at the time it is called (dynamic call). With dynamic loading, the called program is loaded only when it is needed. The link-edit process differs, depending on whether your program uses static calls or dynamic calls.

  • Using a Static Call - A static call occurs when you use the CALL literal statement (where literal is the name of a subprogram) in a program that is compiled with the NODYNAM option. With NODYNAM, all calls of the CALL literal format in the program are treated as static calls. Because a statically called program is link-edited into the same load module as the calling program, a static call is executed more quickly than a dynamic call. A static call is the preferred method if your application does not require the services of the dynamic call. In a non-CICS environment, an assembler macro (IGZBRDGE) can be used to convert some static calls to dynamic calls. The conversion can aid in migration of OS/VS COBOL NORES applications to VS COBOL II RES, DYNAM.
  • Using a Dynamic Call - A dynamic call occurs when you use the CALL literal statement in a program compiled with the DYNAM option, or if you use the CALL identifier statement. A dynamic call loads the subprogram at run time. Use a dynamic call statement when:
  • You are concerned about ease of maintenance. Applications do not have to be re-link-edited when dynamically called subprograms are changed.
  • The subprograms called are used infrequently or are very large. If the subprograms are called only on a few conditions, dynamic calls can bring in the subprogram only when needed. If the subprograms are very large or there are many of them, use of static calls might require too much main storage. Less total storage may be required to call and cancel one, then call and cancel another, than to statically call both.
  • You want to call subprograms in their unused state, and you cannot use the INITIAL attribute. When you cannot use the INITIAL attribute to ensure that a subprogram is placed in its unused state each time it is called, you can set the unused state by using the CALL and CANCEL procedure. For this procedure, use a combination of dynamic CALL and CANCEL statements and compile the program with the RESIDENT option. When you CANCEL a subprogram that was initially called by an OS/VS COBOL or VS COBOL II program, the next CALL will cause the subprogram to be reinitialized to its unused state. Using the CANCEL statement to explicitly cancel a subprogram that was dynamically loaded and branched to by a non-COBOL program does not result in any action being taken to release the subprogram's storage or to delete the subprogram.
  • You have an OS/VS COBOL or other AMODE(24) program in the same run unit with VS COBOL II RES programs that you want to execute in 31-bit addressing mode. Dynamic CALL processing under the VS COBOL II library includes AMODE switching for AMODE(24) programs calling AMODE(31) programs, and vice versa. When performing AMODE switching, control is passed from the caller to a VS COBOL II library routine. After the switching is performed, control is passed to the called program, and the library routine's save area will be positioned between the caller program's save area and called program's save area.
  • The program name to be called is not known until run time. In this case, use the format CALL identifier, where the identifier is adata item that will contain the name of the called program at run time. In terms of practical application, you might use CALL identifier when the program to be called is variable, depending on conditional processing in your program. CALL identifier is always dynamic, even if you use the NODYNAM compiler option. When you use the NODYNAM option, do not mix a dynamic CALL identifier and a static CALL literal for the same subprogram. At best, this wastes space because two copies of the subprogram are loaded into storage, and it does not guarantee that the subprogram will be left in its last-used state. When you have dynamic calls in a program, you must compile it using the RESIDENT option, because dynamic calls are supported only in conjunction with the COBOL Library Management feature. To make all CALL literal calls in a program dynamic, use the compiler option DYNAM.

Performance Considerations of Static and Dynamic Calls

Dynamic calls take more processing than static calls. However, a dynamic call may use less total storage than a static call. Storage usage depends on whether:

  • The subprogram is called only on a few conditions. Regardless of whether it is called or not, a statically called program is loaded into storage; whereas, a dynamically called program is loaded only when it is called.
  • You subsequently delete the dynamically called subprogram with a CANCEL statement. A statically called program cannot be deleted, but a dynamically called program can be deleted. Using a dynamic call and then a CANCEL statement to delete the dynamically called program after it is no longer needed in the application (and not after each CALL to it) may require less storage than using a static call.

Sharing Data Using the EXTERNAL Clause

Separately compiled programs (including programs within abatch sequence) may share data items by use of the EXTERNAL clause. EXTERNAL is specified on the 01-level data description in the Working-Storage section of a program, and the following rules apply:

  • Items subordinate to an EXTERNAL group item are themselves EXTERNAL.
  • The name used for the data item cannot be used on another EXTERNAL item within the same program.
  • The VALUE clause cannot be specified for any group item, or subordinate item, that is EXTERNAL.

Any COBOL program within the run unit, having the same data description for the item as the program containing the item, can access and process the data item. Remember that any program that has access to an EXTERNAL data item can change its value. Do not use this clause for data items you need to protect. EXTERNAL data records are allocated in storage below the 16-megabyte line and thus can be referenced by A MODE(24) subprograms.

Passing Data BY REFERENCE or BY CONTENT

Passing data BY REFERENCE means that the subprogram is referring to and processing the data items in the calling program's storage, rather than working on a copy of the data.

Passing data BY CONTENT means that the calling program is passing only the contents of the literal or identifier. With a CALL ... BY CONTENT, the called program cannot change the value of the literal or identifier in the calling program, even if it modifies the variable in which it received the literal or identifier.

Whether you pass data items BY REFERENCE or BY CONTENT depends on what you want your program to do with the data:

  • If you want the subprogram to receive data from its caller and share it (that is, use the same memory location for it as does the calling program), specify: CALL ... BY REFERENCE identifier. Any changes the subprogram makes to the data affect the data in the calling program. An identifier in the USING phrase of the CALL ... BY REFERENCE statement may be a file-name, in addition to a data-name. If the identifier is the file-name for a queued sequential file, the COBOL compiler passes the address of the data control block (DCB) as this entry of the parameter list. The identifier may not be a VSAM file-name. File-names as CALL operands are allowed by the compiler as an extension. Any use of the extension generally depends on the specific internal implementation of the compiler. Control block field settings may change in future releases. This mechanism cannot be used for file sharing between COBOL programs. This is only for passing data control blocks to assembler programs. Use EXTERNAL or GLOBAL files to implement file sharing between COBOL programs.
  • If you want to pass the address of a record area to a called program, specify: CALL ... BY CONTENT ADDRESS OF record-name. The subprogram receives the ADDRESS special register for the record-name you specify. You must define the record-name as a level-01 or level-77 item in the Linkage Section of the called and calling programs..A separate ADDRESS special register is provided for each record in the Linkage Section.
  • If you want the subprogram to use the data passed from its caller, but do not want the subprogram and calling program to share the same memory for the data, specify: CALL ... BY CONTENT identifier. Any changes in subprogram data do not affect the data in the calling program.
  • If you want to pass a literal value to a called subprogram, specify: CALL ... BY CONTENT literal.
  • If you want to pass the length of a data item, specify: CALL ... BY CONTENT LENGTH OF identifier. The calling program passes the length of the identifier from its LENGTH special register.
  • If you want the subprogram to receive data from its caller and share it, and you want to pass the length of the data to the subprogram, specify a combination of BY REFERENCE and BY CONTENT. For example:

Passing Data BY REFERENCE or BY CONTENT

Data items in a calling program can be described in the Linkage Section of all the programs it calls directly or indirectly. In this case, storage for these items is allocated in the highest calling program. That is, program A calls program B, which calls program C. Data items in program A can be described in the Linkage Sections of programs B and C, and the one set of data can be made available to all three programs. Do not pass parameters allocated in storage 16 megabytes to AMODE(24) subprograms; use the DATA(24) option.

In the calling program, the common data items are described in the Data Division in the same manner as other data items in the Data Division. Unless they are in the Linkage Section, storage is allocated for these items in the calling program. If you reference data in a file, the file must be open when the data is referred to. Code the USING clause of the CALL statement to pass the items.

In the called program, common data items are described in the Linkage Section. Code the USING clause after the PROCEDURE-DIVISION header to receive the data items.

Linkage Section

You must know what is being passed from the calling program and set up the Linkage Section in the called program to accept it. To the called program, it does not matter which clause of the CALL statement you use to pass the data (BY REFERENCE or BY CONTENT). In either case, the called program must describe the data it is receiving. It does this in the Linkage Section.

The number of data-names in the identifier list of a called program must not be greater than the number of data-names in the identifier list of the calling program. There is a one-to-one positional correspondence; that is, the first identifier of the calling program is passed to the first identifier of the called program, and so forth. The compiler makes no attempt to correspond data-names by name.

Grouping Data to Be Passed

Consider grouping all the data items you want to pass between programs and putting them under one level-01 item. If you do this, you can pass a single level-01 record between programs. To make the possibility of mismatched records even smaller, put the level-01 record in a copy library, and copy it in both programs. (That is, copy it in the Working-Storage Section of the calling program and in the Linkage Section of the called program.)


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

IBM Mainframe Topics