Another flow of control issue related to dependency is the prevention of critical block exits. To understand exactly what a critical block exit is and why it is important, you must first understand the following basic concepts:
-
Critical objects
This concept is introduced under Dependency in this section. You should be aware that the critical objects of a process can be stored in more than one process stack, and they can be stored in more than one activation record in a process stack. If any block that declares one of these critical objects is exited, the corresponding activation record is removed and that critical object ceases to exist. This block exit causes the process using that critical object to terminate abnormally.
-
Critical block
This is a block that includes a definition of at least one critical object and is so positioned that it is normally exited before any other blocks that declare critical objects are exited. If you ensure the parent does not exit the critical block prematurely, then the other blocks declaring critical objects also are not exited prematurely.
At this point, the definition of a parent can be further refined as follows: the parent is the process that owns the critical block of a specified process. In other words, the parent has entered the critical block and not yet exited that block. A dependent process is said to be an offspring of its parent.
You need to be concerned with the critical block for a process only if that process is an asynchronous dependent process or a coroutine. If the process is either of these, you must take steps to ensure the critical block is not exited before the process terminates.
By contrast, if a process is independent, it is not affected by critical block exits. If the process is synchronous, then the parent ceases execution until the process terminates and therefore has no opportunity to exit the critical block prematurely.
Effects of a Critical Block Exit
When a parent exits an offspring's critical block, the parent is discontinued and the error message “CRITICAL BLOCK EXIT” is displayed. When the parent terminates, all its offspring processes currently in use are discontinued and a “PARENT PROCESS TERMINATED” error message is displayed.
Defining the Critical Block
The critical block of a process usually occurs somewhere in the program containing the statement that initiated the process. Within that program, the critical block is the procedure of the highest lexical level that contains any of the following items:
-
The declaration of the task variable specified in the process initiation statement.
-
The declaration of the procedure that was initiated, if it is an internal procedure, a passed external procedure, or an imported library procedure. The position of a declared external procedure has no effect on the critical block definition.
-
The declarations of any actual parameters passed to the process by name or by reference. (For call-by-value parameters, the declaration of the actual parameter does not affect the critical block definition.)
-
Any thunk generated for the process by the compiler. A thunk, also referred to as an accidental entry, is generated if the procedure initiation statement passes a constant or an expression to a call-by-name parameter. The thunk is located in the procedure containing the procedure-initiation statement. For an illustration of the effect of a thunk on the critical block definition, refer to Example 3 in “Critical Block Examples” later in this section.
Note that the definition of the critical block can be affected if any of the critical objects are passed as parameters from one procedure to another. If a critical object is passed as a parameter to a procedure, then for purposes of defining the critical block, the formal parameter receiving the critical object must be considered the declaration of that critical object. For an illustration, refer to Example 4 in “Critical Block Examples” later in this section.
There is one exception to the rule about the effects of passing critical objects as parameters. If a task variable is passed as a parameter to an external procedure, the critical block is affected by the declaration of the actual parameter rather than the formal parameter. This exception holds true for all types of external procedures: separate programs, passed external procedures, and imported library procedures. This exception also makes it possible for the procedure-initiation statement to reside in a different program than the critical block does. For an illustration, refer to Example 5 in “Critical Block Examples” later in this section.
The initiator of a process might or might not also be the parent of that process. This issue is illustrated by Examples 1 and 2 in “Critical Block Examples” later in this section.
TYPE Declarations and Critical Blocks
ALGOL and NEWP support special declarations called TYPE declarations. TYPE declarations define a customized class of variables. Later declarations can declare particular variables that are instances of that TYPE. If a variable based on a TYPE is passed to a dependent process as a by-name or by-reference parameter, then the critical block is affected by the location where the particular variable is declared and not the location where the TYPE is declared.
Among the TYPE declarations supported by ALGOL and NEWP is a TYPE called a structure block. Each structure block can include multiple objects of varying kinds, among which could be one or more of the critical objects for a task. The effect of such objects on the critical block definition is determined by the location of the structure block instance, not the structure block type declaration.
For example, suppose there is a structure block type declaration called TASKSTUFF that includes a task variable TVAR. Further, there is a structure block instance called THIS_SB, and an asynchronous task is initiated by a statement such as PROCESS PROC1 [THIS_SB.TVAR]. For purposes of critical block definition, the task variable declaration is considered to reside in the block where THIS_SB is declared.
ALGOL and NEWP also support a TYPE declaration called a connection block. Like a structure block, a connection block can include multiple objects of varying types. However, a connection block is intended for use by connection library declarations. For purposes of critical block definition, any critical objects declared in a connection block are considered to reside where the connection library is declared, not where the connection block is declared.
Preventing ALGOL Critical Block Exits
In ALGOL, the programmer can prevent a critical block exit by including a statement such as the following at the end of the critical block:
WHILE T.STATUS GTR VALUE(TERMINATED) DO
WAITANDRESET(MYSELF.EXCEPTIONEVENT);In this example, T is the task variable of the dependent process. This statement causes the parent to wait on its own EXCEPTIONEVENT task attribute, which is automatically caused by the system whenever the offspring changes status. The program then checks the status of the offspring and returns to a waiting state if the offspring has not yet terminated.
Preventing COBOL Critical Block Exits
The paragraph and section structures supported by COBOL74 and COBOL85 are not blocks and therefore do not affect the critical block definition. Similarly, nested programs in COBOL85 are also not blocks and do not affect the critical block definition. A COBOL process cannot receive a critical block error for exiting any of these types of structures. However, a COBOL process can incur a critical block exit error if the process
-
Terminates while one of its offspring is in-use
-
Exits a bound-in procedure that is the critical block for an offspring
-
Exits an imported library procedure that is the critical block for an offspring
Statements such as the following can be included at the end of a COBOL program to prevent it from terminating before an offspring terminates:
PROCWAIT SECTION.
P2.
WAIT AND RESET UNTIL ATTRIBUTE EXCEPTIONEVENT OF MYSELF.
IF ATTRIBUTE STATUS OF TASK-VAR-1 IS GREATER THAN
VALUE TERMINATED THEN GO PROCWAIT.
STOP RUN.The preceding example assumes that an asynchronous offspring was initiated using task variable TASK-VAR-1. The COBOL program waits on its own EXCEPTIONEVENT task attribute, which is automatically caused whenever the offspring changes status. The program then checks the status of the offspring and returns to a waiting state if the offspring has not yet terminated.
| Note: | The implementation of nested program structures by the COBOL85 compiler is subject to change. Nested programs may affect the critical block definition in future releases. |
Automatic Protection from WFL Critical Block Exits
The programmer does not need to include any special statements in WFL jobs to prevent critical block exits. WFL implicitly waits for the termination of asynchronous processes initiated by the job. The implicit wait occurs at the end of the subroutine that executed the process initiation statement.
Critical Block Examples
The following examples illustrate various factors that affect the definition of the critical block for a process. The more typical cases are presented first.
Example 1
In most cases, the initiator of a process is also the parent of that process. However, this is not always the case. The following ALGOL program is an illustration of the difference between the parent and the initiator:
100 PROCEDURE TRUEPARENT; 110 BEGIN 120 TASK T1, T2; 130 REAL I; 140 150 PROCEDURE WAITFOR(T); 160 TASK T; 170 BEGIN 180 WHILE T.STATUS GTR VALUE(TERMINATED) DO 190 WAITANDRESET(MYSELF.EXCEPTIONEVENT); 200 END; 210 220 PROCEDURE OFFSPRING(X); 230 REAL X; 240 BEGIN 250 X:= 1; 260 END; 270 280 PROCEDURE INITIATOR; 290 BEGIN 300 PROCESS OFFSPRING(I) [T2]; 310 END; 320 330 PROCESS INITIATOR [T1]; 340 WAITFOR(T1); 350 WAITFOR(T2); 360 END.
In this example, the procedure TRUEPARENT initiates the procedure INITIATOR as an asynchronous process. INITIATOR then initiates the procedure named OFFSPRING. In this situation, the initiator of OFFSPRING is INITIATOR, but the parent is TRUEPARENT.
TRUEPARENT is considered the parent because the declarations of the procedure OFFSPRING, the task variable T2, and the actual parameter I all occur in the outer block of TRUEPARENT.
Example 2
In the following ALGOL example, the process called INITIATOR is both the initiator and the parent of the process named OFFSPRING. INITIATOR is considered the initiator because INITIATOR includes the task initiation statement that initiates the OFFSPRING procedure. INITIATOR is considered the critical block for OFFSPRING because the task initiation statement passes OFFSPRING a parameter declared within INITIATOR. An invocation of the WAITFOR procedure is added to INITIATOR to prevent a critical block exit.
100 PROCEDURE OUTERBLOCK; 110 BEGIN 120 TASK T1, T2; 130 140 PROCEDURE WAITFOR(T); 150 TASK T; 160 BEGIN 170 WHILE T.STATUS GTR VALUE(TERMINATED) DO 180 WAITANDRESET(MYSELF.EXCEPTIONEVENT); 190 END; 200 210 PROCEDURE OFFSPRING(X); 220 REAL X; 230 BEGIN 240 X:= 1; 250 END; 260 270 PROCEDURE INITIATOR; 280 BEGIN 290 REAL R; 300 PROCESS OFFSPRING(R) [T2]; 310 WAITFOR(T2); 320 END; 330 340 PROCESS INITIATOR [T1]; 350 WAITFOR(T1); 360 END.
Example 3
The following is an ALGOL example of a case where the presence of a thunk affects the critical block definition for a process:
100 PROCEDURE OUTERBLOCK; 110 BEGIN 120 TASK T1, T2; 130 REAL A, B, C, D; 140 150 PROCEDURE WAITFOR(T); 160 TASK T; 170 BEGIN 180 WHILE T.STATUS GTR VALUE(TERMINATED) DO 190 WAITANDRESET(MYSELF.EXCEPTIONEVENT); 200 END; 210 220 PROCEDURE OFFSPRING(X); 230 REAL X; 240 BEGIN 250 C:= X; 260 END; 270 280 PROCEDURE INITIATOR; 290 BEGIN 300 PROCESS OFFSPRING(A + B) [T2]; 310 WAITFOR(T2); 320 END; 330 340 A:= 2; 350 B:= 5; 360 PROCESS INITIATOR [T1]; 370 WAITFOR(T1); 380 END.
In the preceding example, X is a call-by-name formal parameter of the procedure OFFSPRING. The statement invoking OFFSPRING passes the expression (A + B) to the parameter. This creates a thunk at the point of the procedure initiation, which causes the INITIATOR procedure, rather than the OUTERBLOCK procedure, to be considered the critical block of OFFSPRING. Because the statement at line 360 initiates INITIATOR rather than entering it, INITIATOR becomes a separate process that is the parent of OFFSPRING.
You can avoid some thunks by making the formal parameter call-by-value rather than call-by-name. For example, you can avoid the thunk in the preceding example by adding a line to the procedure heading of the procedure OFFSPRING at line 220. The revised procedure heading appears as follows:
PROCEDURE OFFSPRING(X); VALUE X; REAL X;
This change has the side effect of making OUTERBLOCK the critical block, instead of INITIATOR.
Example 4
In the following ALGOL example, the location of the critical block is affected by a formal parameter specification:
100 PROCEDURE OUTERBLOCK; 110 BEGIN 120 TASK T1, TVAR; 130 REAL I; 140 150 PROCEDURE WAITFOR(T); 160 TASK T; 170 BEGIN 180 WHILE T.STATUS GTR VALUE(TERMINATED) DO 190 WAITANDRESET(MYSELF.EXCEPTIONEVENT); 200 END; 210 220 PROCEDURE OFFSPRING(X); 230 REAL X; 240 BEGIN 250 X:= X + 1; 260 WAIT ((10)); 270 END; 280 290 PROCEDURE INITIATOR(T2); 300 TASK T2; 310 BEGIN 320 PROCESS OFFSPRING(I) [T2]; 330 WAITFOR(T2); 340 END; 350 360 PROCESS INITIATOR(T1) [TVAR]; 370 WAITFOR(TVAR); 380 END.
In this example, INITIATOR is the critical block for the procedure OFFSPRING, because the task variable T2 is declared in the procedure heading of INITIATOR. It makes no difference that the actual parameter T1 is declared in the outer block. It is the formal parameter T2 that is mentioned in the procedure invocation statement, and therefore the declaration of T2 takes precedence.
Example 5
In the following ALGOL examples, the critical block is located in a different program than the one that contains the process-initiation statement. The following is program OBJECT/CALL:
100 BEGIN 110 TASK T, T1; 120 PROCEDURE OB (T); 130 TASK T; 140 EXTERNAL; 150 REPLACE T.NAME BY “OBJECT/CALL/2.”; 160 PROCESS OB (T1) [T]; 170 WHILE T1.STATUS GTR VALUE(TERMINATED) 180 DO WAITANDRESET (MYSELF.EXCEPTIONEVENT); 190 END.
The previous program initiates a separate program called OBJECT/CALL/2, passing a task variable as a parameter. The following is the program OBJECT/CALL/2:
100 PROCEDURE OB (T); 110 TASK T; 120 BEGIN 130 PROCEDURE X; 140 EXTERNAL; 150 REPLACE T.NAME BY “OBJECT/TASK.”; 160 PROCESS X [T]; 170 END.
The preceding program uses its task variable parameter to initiate a third program. The procedure declaration at lines 130 and 140 does not affect the critical block definition, because it is an external procedure declaration. Note that since the process initiation statement is PROCESS, and no WAIT statement follows it, the preceding program finishes executing while the third program, OBJECT/TASK, is still running. However, no CRITICAL BLOCK EXIT error occurs.
The following is the third program, OBJECT/TASK:
100 BEGIN 110 EBCDIC ARRAY FORMALARRAY[0:119]; 120 REPLACE FORMALARRAY BY MYSELF.EXCEPTIONTASK.NAME; 130 DISPLAY (FORMALARRAY); 140 WAIT(MYSELF.ACCEPTEVENT); 150 END.
This program displays the name of its EXCEPTIONTASK, which, by default, is the same as the parent. The name it displays is OBJECT/CALL, which is therefore the parent. Because OBJECT/CALL is the parent, no CRITICAL BLOCK EXIT occurs when OBJECT/CALL/2 terminates.
Recall the rule about passing task variables to external procedures that is discussed under “Defining the Critical Block” earlier in this section. It is the declaration of the actual task variable parameter, at line 110 in OBJECT/CALL, that affects the critical block definition. The critical block is therefore the outer block of OBJECT/CALL. However, the process initiation statement occurs in OBJECT/CALL/2. This is the only type of situation where it is possible for the process-initiation statement and the critical block to reside in separate programs.

