IMPLEMENTING COROUTINES IN COBOL
Coroutines are a simple, efficient and useful form of concurrent programming (although it is better defined as co-operative programming).
Coroutines are like routines but, while there is always a hierarchical relation among common routines in a program, coroutines are all equivalent between themselves.
During the transfer of control from a routine to a second rotutine the caller remembers his state of execution and, when the callee gives up, the caller resumes after the current instruction. On the contrary the callee always resumes at his start.
The peculiarity of coroutines is that, in the exchange of control between coroutines, each coroutine always resumes as a caller routine.
Coroutines are helpful when a complex process could be simplified by decomposition in two o more sub-processes connected by sequential files.
Then these sub-processes can be implemented in the form of coroutines exchanging a virtual sequential files. The files are virtual because the write and the read of the same record happen at the same time and no real file is required.
As an example, we can employ coroutines in place of the awkward "program inversion" of JSP methodology (Jackson Structured Programming). The processes derived from data structures are straightforth converted in coroutines connected by virtual sequential files, without compromising the isomorphisms between data structures and respective algorithms.
Coroutines, although very useful, are supported only by a limited number of programming languages because they require a complex stack management.
COBOL does'nt directly support coroutines, but their implementation is amazingly easy by the exploitation of PERFORM features.
The following program example is the simplest implementation of coroutines in COBOL.
IMPLEMENTATION
The program contains a MAIN and two coroutines, say PROCESS-A and PROCESS-B. The first coroutine makes three virtual writes, the second makes three virtual reads. Every transfer of control from PROCESS-A to PROCESS-B corresponds to a virtual write in PROCESS-A and a virtual read in PROCESS-B. Every tranfer of control from PROCESS-B to PROCESS-A is a request for a new record.
The records are implicitly represented by the whole memory shared by the two processes.
The transfer of control from PROCESS-A to PROCESS-B is obtained by:
The transfer of control from PROCESS-B to PROCESS-A is obtained by:
Note that the transfer of control, in both directions, is always achieved by performing the same labels but in inverted order. This may sound rather strange, but it conforms to COBOL standards. Note also that at the end of processes none of the THRU-label remains suspended.
Following are the displays produced by the example:
[/code]
<< 1
>> 1
<< 2
>> 2
<< 3
>> 3
[/code]
The follwing map represents the sequence of instructions execution, according to the row numeration in the previous example:
The machinery of virtual WRITE/READ can be used in structured instructions like IFs, EVALUATEs, PERFORMs (both forms in-line and off-line) as shows next example:
This example produces this output:
Note as each coroutines have the same structure held by stand-alone unrelated writing and reading processes, although their executions are embedded.
It is simple to make a more complex net of coroutines where each node of the net is a coroutine receiving real inputs and/or virtual inputs from one or more coroutines and writing real outputs and/or one or more virtual outputs to the same number of coroutines.
WARNING: Some COBOL compilers may support non-ANSI compliant behaviours of the PERFORM statement. This may affect the correct working of coroutines.
For example, to assure the ANSI compliant behaviour, CA-Realia requires the NOCALL compiler directive and Micro Focus requires TRICKLE.
COBOL II and COBOL/390 do not require any measure.
Coroutines are a simple, efficient and useful form of concurrent programming (although it is better defined as co-operative programming).
Coroutines are like routines but, while there is always a hierarchical relation among common routines in a program, coroutines are all equivalent between themselves.
During the transfer of control from a routine to a second rotutine the caller remembers his state of execution and, when the callee gives up, the caller resumes after the current instruction. On the contrary the callee always resumes at his start.
The peculiarity of coroutines is that, in the exchange of control between coroutines, each coroutine always resumes as a caller routine.
Coroutines are helpful when a complex process could be simplified by decomposition in two o more sub-processes connected by sequential files.
Then these sub-processes can be implemented in the form of coroutines exchanging a virtual sequential files. The files are virtual because the write and the read of the same record happen at the same time and no real file is required.
As an example, we can employ coroutines in place of the awkward "program inversion" of JSP methodology (Jackson Structured Programming). The processes derived from data structures are straightforth converted in coroutines connected by virtual sequential files, without compromising the isomorphisms between data structures and respective algorithms.
Coroutines, although very useful, are supported only by a limited number of programming languages because they require a complex stack management.
COBOL does'nt directly support coroutines, but their implementation is amazingly easy by the exploitation of PERFORM features.
The following program example is the simplest implementation of coroutines in COBOL.
IMPLEMENTATION
The program contains a MAIN and two coroutines, say PROCESS-A and PROCESS-B. The first coroutine makes three virtual writes, the second makes three virtual reads. Every transfer of control from PROCESS-A to PROCESS-B corresponds to a virtual write in PROCESS-A and a virtual read in PROCESS-B. Every tranfer of control from PROCESS-B to PROCESS-A is a request for a new record.
The records are implicitly represented by the whole memory shared by the two processes.
The transfer of control from PROCESS-A to PROCESS-B is obtained by:
Code:
PERFORM ENTER-PROCESS-B THRU EXIT-PROCESS-B
Code:
PERFORM EXIT-PROCESS-B THRU ENTER-PROCESS-B
Note that the transfer of control, in both directions, is always achieved by performing the same labels but in inverted order. This may sound rather strange, but it conforms to COBOL standards. Note also that at the end of processes none of the THRU-label remains suspended.
Code:
01 main.
02 perform enter-process-a thru exit-process-a
03 goback
04 .
05
06 enter-process-a.
07 begin-process-a.
08 display '<< 1'
09 perform enter-process-b thru exit-process-b
10 display '<< 2'
11 perform enter-process-b thru exit-process-b
12 display '<< 3'
13 perform enter-process-b thru exit-process-b
14 .
15 exit-process-a.
16 .
17
18 enter-process-b.
19 begin-process-b.
20 display ' >> 1'
21 perform exit-process-b thru enter-process-b
22 display ' >> 2'
23 perform exit-process-b thru enter-process-b
24 display ' >> 3'
25 perform exit-process-b thru enter-process-b
26 .
27 exit-process-b.
Following are the displays produced by the example:
[/code]
<< 1
>> 1
<< 2
>> 2
<< 3
>> 3
[/code]
The follwing map represents the sequence of instructions execution, according to the row numeration in the previous example:
Code:
MAIN 01 02 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 03
PROCESS-A .. .. 06 07 08 09 .. .. .. .. .. 10 11 .. .. .. .. 12 13 .. .. .. .. 15
PROCESS-B .. .. .. .. .. .. 18 19 20 21 27 .. .. 18 22 23 27 .. .. 18 24 25 27
The machinery of virtual WRITE/READ can be used in structured instructions like IFs, EVALUATEs, PERFORMs (both forms in-line and off-line) as shows next example:
Code:
main.
perform enter-process-a thru exit-process-a
goback
.
enter-process-a.
begin-process-a.
display 'start process-a'
set process-on to true
perform varying i from 1 by 1 until i > 3
display '<< ' i
perform enter-process-b thru exit-process-b
end-perform
set process-off to true
perform enter-process-b thru exit-process-b
display 'end process-a'
.
exit-process-a.
.
enter-process-b.
begin-process-b.
display 'start process-b'
perform until process-off
display ' >> ' i
perform exit-process-b thru enter-process-b
end-perform
display 'end process-b'
.
exit-process-b.
This example produces this output:
Code:
start process-a
<< 0000000001
start process-b
>> 0000000001
<< 0000000002
>> 0000000002
<< 0000000003
>> 0000000003
end process-b
end process-a
Note as each coroutines have the same structure held by stand-alone unrelated writing and reading processes, although their executions are embedded.
It is simple to make a more complex net of coroutines where each node of the net is a coroutine receiving real inputs and/or virtual inputs from one or more coroutines and writing real outputs and/or one or more virtual outputs to the same number of coroutines.
WARNING: Some COBOL compilers may support non-ANSI compliant behaviours of the PERFORM statement. This may affect the correct working of coroutines.
For example, to assure the ANSI compliant behaviour, CA-Realia requires the NOCALL compiler directive and Micro Focus requires TRICKLE.
COBOL II and COBOL/390 do not require any measure.