When and how to use static or dynamic calls!
Information related to the discussion following:
- The main program with a static call is called: STATICCALL.
- The main program with a dynamic call is called: DYNAMICCALL.
- The called (sub)program is called: RETURNLOWEST.
- RETURNLOWEST takes 2 numbers (number1 and number2) as input fields and returns the lowest of the 2 numbers into an output field (called theLowest).
=======================================
IDENTIFICATION DIVISION.
PROGRAM-ID. STATICCALL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 workData.
05 number1 PIC s9(09) VALUE +1234.
05 number2 PIC s9(09) VALUE -9876.
05 theLowest PIC s9(09).
PROCEDURE DIVISION.
*// RETURNLOWEST is between quotes.
*// This means that the (sub)program called is hardcoded(!)
*// in this source (it is a constant).
*// All hardcoded(!) calls are static calls.
CALL 'RETURNLOWEST' USING
, by content number1
, by content number2
, by reference theLowest
END-CALL
DISPLAY 'The lowest of the 2 numbers is: ' theLowest
GOBACK
.
END PROGRAM. STATICCALL.
=======================================
IDENTIFICATION DIVISION.
PROGRAM-ID. DYNAMICCALL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 workData.
05 someProgram PIC X(30) VALUE 'RETURNLOWEST'.
05 number1 PIC s9(09) VALUE +1234.
05 number2 PIC s9(09) VALUE -9876.
05 theLowest PIC s9(09).
PROCEDURE DIVISION.
*// someProgram is a variable field.
*// This means that this field value must be 'loaded' with the
*// program name before actually calling the (sub)program.
*//
*// This can be done by an initial VALUE (as is the case here)
*// or by a hardcoded MOVE statement, like:
*//
*// MOVE 'RETURNLOWEST' TO someProgram
*//
*// Notice that PIC x(30) is used for the field someProgram.
*// That is OK!
*// This field should be at least as big as the value it must
*// contain.
CALL someProgram USING
, by content number1
, by content number2
, by reference theLowest
END-CALL
DISPLAY 'The lowest of the 2 numbers is: ' theLowest
GOBACK
.
END PROGRAM. DYNAMICCALL.
=======================================
IDENTIFICATION DIVISION.
PROGRAM-ID. RETURNLOWEST.
DATA DIVISION.
LINKAGE SECTION.
01 number1 PIC s9(09).
01 number2 PIC s9(09).
01 theLowest PIC s9(09).
PROCEDURE DIVISION USING number1, number2, theLowest.
IF number1 < number2 THEN
, MOVE number1 TO theLowest
ELSE
, MOVE number2 TO theLowest
END-IF
GOBACK
.
END PROGRAM. RETURNLOWEST.
=======================================
Tip:
Always use dynamic calls!
(as far as I can judge there could possibly one minor exception to this rule, which is:
Use static call only for returning a single static error message in case of a complete systems failure...which...as a rule and in practice...should never occur!).
How does a static call work?
Answer:
At compile time every statically called subprogram will be copied and statically linked into programs calling these statically called programs.
This means when you have 100 programs calling 'RETURNLOWEST' statically that the executable code of 'RETURNLOWEST' is copied 100 times and added to each and every one of these 100 (main)programs (to be more precise, in the linkage step of the whole compile proces).
Consequences:
It increases the loadmodule size of each and every one of these 100 programs.
And when you change and recompile the subprogram 'RETURNLOWEST' then you need to again re-link all the 100 programs that include this static call.
Advantage of a static link:
You are 100% sure that this code can always be executed.
This is because a copy of the static program is actually a part of the main program. Hence, when the main program is loaded into main storage (RAM) then the copy is also loaded. 100% guarantee!
This, and only this, is the reason why a static link is good for a 'fatal-error-message' in case of a complete systems failure. However, there are no other advantages (note: some may say that a static link is quicker, more performant, but in (most) real programming environments this is a non-issue and mostly it is not even true!).
Disadvantages of static link:
First (and very important): very maintenance intensive.
Secondly: Main storage may be overloaded with many copies of the same static executable code. Again, (advise) never use static calls (with the possible exception previously mentioned).
How does a dynamic call work?
Answer:
(With reference to the static call explanation) a dynamic call is resolved at execution time.
With the first call the subprogram module is loaded into main storage (RAM) and then executed.
With every second and subsequent call it is just executed from main storage (because it already is and stays loaded into main storage).
For every dynamically called (sub)program there is always only one loadmule in main storage. Regardless how many different users are calling it in regardless how many different ways.
Advantage of dynamic calls:
Only one copy in main storage (as said before).
You can change and recompile dynamic loadmodules anytime.
There is no need to relink or recompile any module that calls this loadmodule.
You can make changes immediately effective without stopping the system by just reloading the changed module (changed subprogram). As of that moment all running main programs will immediately use this new, modified, copy (note: this feature is nice...but it is not wise to use it directly in a production environment without testing the new or changed functionality first on a test system).
What does the 'by content' or 'by reference' mean, or what does it do, as used in the CALL statement?
Answer:
'by content' means that when the call is executed a copy is taken of this variable.
That is; the system will dynamically allocate some storage and physically take a copy and then and only then passes this copy to the called (sub)program. The dynamically allocated storage is freed upon return of the called (sub)program. This means that the original value is not changed, regardless how the copy of the storage area was used by the called (sub)program.
Using 'by content' can, and does, make your program more robust. This is definitely a good thing...however...using the 'by content' option abundently may affect your storage consumption.
'By reference' means that the address of the field involved is passed to the called (sub)program. This means that the called (sub)program can directly change the contents of this memory address location (note: the addresses, as discussed here, are automatically resolved by COBOL. The COBOL language also include some instructions to directly manipulate address locations...but that is a different issue!).
Be aware of the following rules for the 'by content' and 'by reference' option:
1. The default is 'by reference'.
2. When either the 'by content' or 'by reference' option is used explicitly then that will become the default.
The latter can best be explained using an example:
call-1:
CALL 'RETURNLOWEST' USING
, by content number1
, by content number2
, by reference theLowest
END-CALL
call-2:
CALL 'RETURNLOWEST' USING
, by content number1
, number2
, by reference theLowest
END-CALL
call-3: --- NOTE: CALL-3 IS NOT CORRECT!!! --->
CALL 'RETURNLOWEST' USING
, by content number1
, number2
, theLowest
END-CALL
call-4:
CALL 'RETURNLOWEST' USING
, number1
, number2
, theLowest
END-CALL
call-1 and call-2 are exactly(!) the same.
number1 in call-1 has the 'by content' phrase.
number2 in call-1 does not have the 'by content' or 'by reference' phrase. It will use whatever was used before in the call statement. Therefore, in this case, it uses the 'by content'.
If no 'by xxxxxxx' phrase was specified at all then the standard default is used.
The standard default is: 'by reference' (see call-4).
call-3 will never produce anything useful! Why?
answer: The 'by content' is used for the first field.
As of that moment the 'by content' becomes the default for that specific call.
theLowest field is supposed to be a return field. However, in this case the 'by content' is still default.
The called (sub)program will function perfectly well. But it works with copies only, including a copy of the return field. Upon return the storage is freed for all copies of the 'by content' fields and the original 'by content' fields will be unchanged. As a result the original theLowest field is unchanged.
call-4 will work.
In call-4 all fields are 'by reference', which is the default.
Though there is a danger here! number1 and number2 are supposed to be input only fields. However, nothing will prevent the called (sub)program for executing the following move statement:
MOVE ZEROS TO number1, number2
So, your main program may not automatically assume that these input fields are unchanged upon return of the called (sub)program.
I hope the explanation above was informative (to some)
Regards, Wim Ahlers.
Information related to the discussion following:
- The main program with a static call is called: STATICCALL.
- The main program with a dynamic call is called: DYNAMICCALL.
- The called (sub)program is called: RETURNLOWEST.
- RETURNLOWEST takes 2 numbers (number1 and number2) as input fields and returns the lowest of the 2 numbers into an output field (called theLowest).
=======================================
IDENTIFICATION DIVISION.
PROGRAM-ID. STATICCALL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 workData.
05 number1 PIC s9(09) VALUE +1234.
05 number2 PIC s9(09) VALUE -9876.
05 theLowest PIC s9(09).
PROCEDURE DIVISION.
*// RETURNLOWEST is between quotes.
*// This means that the (sub)program called is hardcoded(!)
*// in this source (it is a constant).
*// All hardcoded(!) calls are static calls.
CALL 'RETURNLOWEST' USING
, by content number1
, by content number2
, by reference theLowest
END-CALL
DISPLAY 'The lowest of the 2 numbers is: ' theLowest
GOBACK
.
END PROGRAM. STATICCALL.
=======================================
IDENTIFICATION DIVISION.
PROGRAM-ID. DYNAMICCALL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 workData.
05 someProgram PIC X(30) VALUE 'RETURNLOWEST'.
05 number1 PIC s9(09) VALUE +1234.
05 number2 PIC s9(09) VALUE -9876.
05 theLowest PIC s9(09).
PROCEDURE DIVISION.
*// someProgram is a variable field.
*// This means that this field value must be 'loaded' with the
*// program name before actually calling the (sub)program.
*//
*// This can be done by an initial VALUE (as is the case here)
*// or by a hardcoded MOVE statement, like:
*//
*// MOVE 'RETURNLOWEST' TO someProgram
*//
*// Notice that PIC x(30) is used for the field someProgram.
*// That is OK!
*// This field should be at least as big as the value it must
*// contain.
CALL someProgram USING
, by content number1
, by content number2
, by reference theLowest
END-CALL
DISPLAY 'The lowest of the 2 numbers is: ' theLowest
GOBACK
.
END PROGRAM. DYNAMICCALL.
=======================================
IDENTIFICATION DIVISION.
PROGRAM-ID. RETURNLOWEST.
DATA DIVISION.
LINKAGE SECTION.
01 number1 PIC s9(09).
01 number2 PIC s9(09).
01 theLowest PIC s9(09).
PROCEDURE DIVISION USING number1, number2, theLowest.
IF number1 < number2 THEN
, MOVE number1 TO theLowest
ELSE
, MOVE number2 TO theLowest
END-IF
GOBACK
.
END PROGRAM. RETURNLOWEST.
=======================================
Tip:
Always use dynamic calls!
(as far as I can judge there could possibly one minor exception to this rule, which is:
Use static call only for returning a single static error message in case of a complete systems failure...which...as a rule and in practice...should never occur!).
How does a static call work?
Answer:
At compile time every statically called subprogram will be copied and statically linked into programs calling these statically called programs.
This means when you have 100 programs calling 'RETURNLOWEST' statically that the executable code of 'RETURNLOWEST' is copied 100 times and added to each and every one of these 100 (main)programs (to be more precise, in the linkage step of the whole compile proces).
Consequences:
It increases the loadmodule size of each and every one of these 100 programs.
And when you change and recompile the subprogram 'RETURNLOWEST' then you need to again re-link all the 100 programs that include this static call.
Advantage of a static link:
You are 100% sure that this code can always be executed.
This is because a copy of the static program is actually a part of the main program. Hence, when the main program is loaded into main storage (RAM) then the copy is also loaded. 100% guarantee!
This, and only this, is the reason why a static link is good for a 'fatal-error-message' in case of a complete systems failure. However, there are no other advantages (note: some may say that a static link is quicker, more performant, but in (most) real programming environments this is a non-issue and mostly it is not even true!).
Disadvantages of static link:
First (and very important): very maintenance intensive.
Secondly: Main storage may be overloaded with many copies of the same static executable code. Again, (advise) never use static calls (with the possible exception previously mentioned).
How does a dynamic call work?
Answer:
(With reference to the static call explanation) a dynamic call is resolved at execution time.
With the first call the subprogram module is loaded into main storage (RAM) and then executed.
With every second and subsequent call it is just executed from main storage (because it already is and stays loaded into main storage).
For every dynamically called (sub)program there is always only one loadmule in main storage. Regardless how many different users are calling it in regardless how many different ways.
Advantage of dynamic calls:
Only one copy in main storage (as said before).
You can change and recompile dynamic loadmodules anytime.
There is no need to relink or recompile any module that calls this loadmodule.
You can make changes immediately effective without stopping the system by just reloading the changed module (changed subprogram). As of that moment all running main programs will immediately use this new, modified, copy (note: this feature is nice...but it is not wise to use it directly in a production environment without testing the new or changed functionality first on a test system).
What does the 'by content' or 'by reference' mean, or what does it do, as used in the CALL statement?
Answer:
'by content' means that when the call is executed a copy is taken of this variable.
That is; the system will dynamically allocate some storage and physically take a copy and then and only then passes this copy to the called (sub)program. The dynamically allocated storage is freed upon return of the called (sub)program. This means that the original value is not changed, regardless how the copy of the storage area was used by the called (sub)program.
Using 'by content' can, and does, make your program more robust. This is definitely a good thing...however...using the 'by content' option abundently may affect your storage consumption.
'By reference' means that the address of the field involved is passed to the called (sub)program. This means that the called (sub)program can directly change the contents of this memory address location (note: the addresses, as discussed here, are automatically resolved by COBOL. The COBOL language also include some instructions to directly manipulate address locations...but that is a different issue!).
Be aware of the following rules for the 'by content' and 'by reference' option:
1. The default is 'by reference'.
2. When either the 'by content' or 'by reference' option is used explicitly then that will become the default.
The latter can best be explained using an example:
call-1:
CALL 'RETURNLOWEST' USING
, by content number1
, by content number2
, by reference theLowest
END-CALL
call-2:
CALL 'RETURNLOWEST' USING
, by content number1
, number2
, by reference theLowest
END-CALL
call-3: --- NOTE: CALL-3 IS NOT CORRECT!!! --->
CALL 'RETURNLOWEST' USING
, by content number1
, number2
, theLowest
END-CALL
call-4:
CALL 'RETURNLOWEST' USING
, number1
, number2
, theLowest
END-CALL
call-1 and call-2 are exactly(!) the same.
number1 in call-1 has the 'by content' phrase.
number2 in call-1 does not have the 'by content' or 'by reference' phrase. It will use whatever was used before in the call statement. Therefore, in this case, it uses the 'by content'.
If no 'by xxxxxxx' phrase was specified at all then the standard default is used.
The standard default is: 'by reference' (see call-4).
call-3 will never produce anything useful! Why?
answer: The 'by content' is used for the first field.
As of that moment the 'by content' becomes the default for that specific call.
theLowest field is supposed to be a return field. However, in this case the 'by content' is still default.
The called (sub)program will function perfectly well. But it works with copies only, including a copy of the return field. Upon return the storage is freed for all copies of the 'by content' fields and the original 'by content' fields will be unchanged. As a result the original theLowest field is unchanged.
call-4 will work.
In call-4 all fields are 'by reference', which is the default.
Though there is a danger here! number1 and number2 are supposed to be input only fields. However, nothing will prevent the called (sub)program for executing the following move statement:
MOVE ZEROS TO number1, number2
So, your main program may not automatically assume that these input fields are unchanged upon return of the called (sub)program.
I hope the explanation above was informative (to some)
Regards, Wim Ahlers.