Category : Modula II Source Code
Archive   : M2TUTOR.ZIP
Filename : CHAP15.TXT

 
Output of file : CHAP15.TXT contained in archive : M2TUTOR.ZIP

Chapter 15

CONCURRENCY



PREREQUISITES FOR THIS MATERIAL
______________________________________________________________

In order to understand this material you should have a good
grasp of the material in Part I of this tutorial, and at least
a cursory knowledge of the material in chapter 14 concerning
the pseudo module SYSTEM and especially the ADR and SIZE
functions.


WHAT IS CONCURRENCY
______________________________________________________________

True concurrency is when two processes are taking place at
exactly the same time with neither affecting the other in a
degrading way. This is possible in many areas of your life,
such as when the grandfather clock is running at the same time
as the furnace and the television set. These are different
processes all running at the same time. In the field of
computers, true concurrency requires at least two separate
processors that are running completely separate programs or
different parts of the same program.

Usually, when computers are said to be running concurrent
processes, reference is being made to a time sharing situation
in which two or more programs are swapped in and out of the
computer at a rapid rate, each getting a small slice of time,
a few milliseconds being typical. Each process appears to be
running constantly but is in fact only running a small part
of the time, sort of stuttering. The swapping in and out is
taking place so fast that, to the casual user, it appears that
the entire computer is working on only his program.
Concurrent programs must deal with asynchronous events that
occur at arbitrary times.



WHAT IS MODULA-2 CONCURRENCY?
______________________________________________________________

Most literature about Modula-2 uses the term concurrency to
discuss the topic of two or more processes operating at the
same apparent time, but the term coprocessing is more
descriptive and will be used in this tutorial. Coprocessing
can be defined as two or more asynchronous processes happening
in such a way that they interact in some way, so that some
level of coordination must be carried on between them. An
example would be a text processor that is busy updating the
video monitor between input keystrokes.

15-1

Chapter 15 - Concurrency

Modula-2 provides the necessary constructs to implement
coprocesses. We will define and illustrate most of them in
this chapter but we will begin with a short example program
as we have with all new topics.


OUR FIRST COROUTINE
______________________________________________________________

Examine the program named COROUT.MOD for ================
our first look at this new technique. COROUT.MOD
Without lots of explanation, there are ================
several new items imported and a few
variables defined, the most interesting being the three
variables declared that are of type PROCESS. These will be
the three process variables we will use in this example, and
we will say more about the process type variable later. There
are two procedures defined in lines 13 through 36 which define
what the processes will do. It must be noted that these
procedures must be parameterless, no formal parameters
allowed. Many of the details of these procedures will be
explained in the next few paragraphs. Finally we come to the
main program which is where we will start to define how to use
the two processes.

In the main program, we call the procedure NEWPROCESS which
loads a new process without running it. We give it the name
of the procedure where the process can be found, in this case
MainProcess, and give the system a workspace by assigning a
previously defined array to it. This is done by giving the
system the address and the size of the array in the next two
actual parameters. Finally, we give it the name of the
process variable. Note that the workspace must be sufficient
for the new process to store all of its variables, so give the
process a generous workspace. In this case, we give the
workspace 300 words. If it is too small, a runtime error will
be generated. We then load a second process in preparation
for running the program by calling NEWPROCESS again with
different formal parameters for the other process named
SubProcess.


WHAT DID NEWPROCESS DO?
______________________________________________________________

Each time we call NEWPROCESS, it loads a copy of the procedure
named in the first actual parameter into the address space of
the system at the location specified by the second actual
parameter. It also stores a copy of the starting address of
this procedure in the process variable named in the fourth
actual parameter. The process variable is a record that can
store the entire state of a process. We will see more about
this record variable in the remainder of this chapter.


15-2

Chapter 15 - Concurrency

The end result of execution of both procedure calls in lines
39 and 41 is that we have two procedures stored along with all
of their local variables, in this case only the single
variable defined in line 13, and we have two process variables
stored.


HOW DO WE GET IT STARTED?
______________________________________________________________

When we began executing the main program, we actually loaded
and started a process, the one we are presently executing and
this process stays resident in memory and active throughout
the period of execution of the program. We have done this for
every program in this tutorial so far but paid no attention
to it as far as referring to it as a process. To transfer
control to a different process, we use the transfer function
with the name of a process variable for the process we wish
to terminate and another process variable for the process we
wish to start. The current state of the hardware processor
is stored in the first process variable mentioned, and the
state of the second process variable named is loaded into the
hardware processor. This effectively transfers control to the
second process.

This is illustrated in line 43 from which we jump to line 15
in the process named Process1. In Process1 we begin a FOR
loop where we write a string and a cardinal number then when
Index exceeds 2, we do another transfer, this time from
Process1 to Process2. The transfer procedure in line 19
stores the current state of the hardware processor in the
process variable named Process1 and retrieves the machine
state from Process2, stores it in the hardware processor, and
jumps to the new process. The jump is effected automatically
since the instruction counter is part of the process variable.
Thus we jump from line 19 in the first procedure to line 31
in the second where we begin an infinite loop. We display
another string in line 32 and once again execute another
transfer from line 33 to somewhere. This transfer causes the
current state of the hardware processor to be stored in
Process2 and retrieves the state from process1 and stores it
in the hardware processor. Since the state stored in the
process variable named Process1 was stored there at statement
19, the address of the next line is a part of this state and
the program counter is automatically set to the address where
line 20 is stored and execution is returned there.


WHERE DO WE RETURN TO A COROUTINE?
______________________________________________________________

Instead of jumping to the beginning of the procedure named
MainProcess again, which would be line 15 in this example, we
return to the statement following the one we transferred away

15-3

Chapter 15 - Concurrency

from. In this case we will return from line 33 to line 20
and complete the loop we started earlier. When Index is
increased to 4, we will once again transfer control from line
19 to the state stored in Process2, but instead of jumping to
line 31 we will return where we left off there at line 34.
That loop will complete, and we will once again return to line
20. When the for loop in MainProcess finishes, we execute
lines 24 and 25 in normal sequential fashion, then the
transfer in line 26 will load the data stored in the process
variable named main effectively returning control to the main
module body where we will arrive at line 44. From there we
complete the last two write statements, then do a normal exit
to the operating system.

This example of coprocess usage was defined step by step as
a beginning into this topic. If it was not clear, reread the
entire description until you understand it. There is really
nothing else to know about coroutines other than the knowledge
gained by experience in using them, which is true of any new
principle in computer programming or any other facet of life.


WHAT WAS ALL THAT GOOD FOR?
______________________________________________________________

The thing that is really different about coroutines is the
fact that they are both (or all three or more) loaded into the
computers memory, and they stay loaded for the life of the
program along with their local variables. This is not true
of normal procedures. It is transparent to you, but variables
local to a procedure are not all simply loaded into the
computer and left there. They are dynamically allocated and
used each time the procedure is called, then discarded when
the procedure completes its job. That is why variables are
not saved from one invocation of a procedure to the next. The
variables which are defined in the outer layer of a process
are loaded once, and stay resident for the life of the
process.

In the example program under consideration, all three
processes including the main program are loaded and none is
really the main program since any of the programs have the
ability to call or control the others. Rather than the strict
hierarchy of calling, as is done with procedures, the
coroutines are all at the same hierarchical level.

Examine the program named COROUT2.MOD for ===============
the second example with coroutines. This COROUT2.MOD
program is identical with the last one ===============
except that two lines are removed from the
first process and placed in a normal procedure which is called
from line 22 to illustrate that some of the code can be placed
outside of the coroutine process to make the resident part
smaller. The implication here is that you could have several

15-4

Chapter 15 - Concurrency

coprocesses going on, each of which had a very small resident
portion, and each of which called some large regular
procedures. In fact, there is no reason why two or more of
the coprocesses could not call the same normal procedure.

Study this program until you understand the implications, then
compile and execute it to see that it does exactly the same
thing as the last program.


HOW ABOUT THREE PROCESSES?
______________________________________________________________

Examine the example program named ===============
COROUT3.MOD for an example with three COROUT3.MOD
concurrent processes. This program is ===============
identical to the first program with the
addition of another process. In this program, Process1 calls
Process2, which calls Process3, which once again calls
Process1. Thus the same loop is traversed but with one
additional stop along the way. It should be evident to you
that there is no reason why this could not be extended to any
number of processes each calling the next or in any looping
scheme you could think up provided of course that it had some
utilitarian purpose. It is not apparent, but there are
actually four coprocesses here because the main program, in
lines 47-57, qualifies to be called another process. This
will be very graphically demonstrated in the next example
program because the main program is the main control loop.


SORT OF RANDOM PROCESSES
______________________________________________________________

Examine the program named RANPROG.MOD for ===============
an example of coprocessing that is a RANPROG.MOD
little more irregular than the very ===============
repeatable loops of the first three
example programs in this chapter. Note that the main program
is one of the major contributors to this program because it
contains a loop which calls each of the other processes once
during each pass through the loop, with WriteString calls
interleaved between the calls. The WriteString calls are
obviously for the purpose of tracking program execution later.

Each call to Hello results in writing the word "hello" to the
monitor and a return to the main program, but each call to
Toggle results in either a return to the main program or a
transfer to Hello after writing a different word to the
monitor in each case. Which path is taken is controlled by
the local variable ToggleFactor which is toggled each time the
Toggle procedure is called. If ToggleFactor is zero, the word
"even" is written to the monitor, and control is transferred
to Hello, which writes "hello" and transfers directly back to

15-5

Chapter 15 - Concurrency

the main program without returning through the Toggle
procedure. When ToggleFactor is one, the word "odd" is
displayed and control is transferred back to the main program.

The observant student will notice that we return to each
process at the statement following where we transferred away
and in the case of the procedure named toggle, we actually
return inside of a loop and inside of an if statement. The
Modula-2 system is designed to handle this for us, and it is
because of this capability that we can write true coprocessing
programs in Modula-2. The time it takes you to understand
this program will be well spent since it is the only program
in this tutorial that depicts the value of the concurrent
capabilities of Modula-2.

The interested student will find many books and articles on
coprocessing and parallel operation in general.


WHAT IS THE BIG DIFFERENCE?
______________________________________________________________

Actually, the big difference between real concurrent
processing and what this is doing is in the division of time
and in who, or what, is doing the division. In real
concurrent processing, the computer hardware is controlling
the operation of the processing and allocating time slots to
the various processes. In the pseudo concurrent processing
we are doing, it is the processes themselves that are doing
the time allocation leading to the possibility that if one of
the processes went bad, it could tie up all of the resources
of the system and no other process could do anything. You
could consider it a challenge to write a successful system
that really did share time and resources well.

The important thing to consider about this technique is that
it is one additional tool available in Modula-2 that is not
available in the usual implementations of other popular
languages such as Pascal, C, Fortran, or BASIC.


ONE MORE INFINITE EXAMPLE OF CONCURRENCY
______________________________________________________________

Examine the program named INFINITE.MOD for ================
another example of a program with INFINITE.MOD
concurrency. In this program, three ================
processes are created and control is
transferred to the first one after which they call each other
in a loop with no provision for ever returning to the main
program. The computer will continually loop around the three
processes checking the printer, the keyboard, and the system
clock to see if they need servicing. It must be pointed out
that it would be a simple matter to include all three checks

15-6

Chapter 15 - Concurrency

in a single loop in the main program and do away with all of
this extra baggage. This method had one advantage over the
simple loop and that is the fact that each coprocess can have
its own variables which cannot be affected by the operation
of the other processes and yet are all memory resident at all
times.

This program can be compiled and run but it will execute
forever since it has no way to stop it. You must stop it
yourself by using the method of program abort provided on your
system. If you are using MS-DOS, a control-break combination
will probably terminate the program. It should be mentioned
that if this technique were used in a real situation, it would
not be writing to the monitor continually.


IS THIS REALLY USEFUL?
______________________________________________________________

In a situation where you needed to service interrupts in some
prescribed and rapid fashion, a technique involving Modula-2
concurrency could prove to be very useful. There are other
procedures available in Modula-2 to aid in writing a
pseudo-concurrent program but they are extremely advanced and
would require an intimate knowledge of your particular systems
hardware, especially the interrupt system. The one procedure
available for this is the IOTRANSFER procedure which is used
to establish the interface to a hardware interrupt.

Since using these techniques are extremely advanced
programming techniques, they are beyond the scope of this
tutorial and will not be covered here.


WHAT ABOUT USING THESE PROCEDURES
______________________________________________________________

In order to effectively use the techniques of coprocessing,
you will need to devote a great deal of study to parallel
processing. Many terms will be encountered, such as shared
variables, semaphores, deadlock, etc. Each of these terms
involve techniques on programming problems that are beyond the
scope of this tutorial since our goal is to teach you the use
of various constructs in Modula-2, not the overall system
design problem.

Chapter 16 of this tutorial gives you practical examples of
the use of Modula-2.





15-7



  3 Responses to “Category : Modula II Source Code
Archive   : M2TUTOR.ZIP
Filename : CHAP15.TXT

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/