CPL 24: Variations in Subprogram Control
Review
Synopsis
References: Pratt &Zelkowitz 9.1, 9.2
Exception Handling
You have already come across the idea of exceptions in your programming,
whenever your program tried to read an alphabetic character when an integer
was expected, or to divide by zero. It is highly desirable for a language to
provide mechanisms for recovering from such conditions, via exception
handlers. It may also be desirable for a language to provide means for
programmers to define further exceptions in addition to the forms detected by
hardware or operating system described above.
A couple of aspects of exceptions are implemented differently across
languages
Coroutines
Coroutines are program units
that have intertwining execution sequences, but where only one is in
execution at one time.
A coroutine has multiple entry points, which are controlled
by the coroutine itself; it also has a means to maintain its status between
activations. Coroutines often contain an initialisation segment, executed only
the first time the coroutine is called. Typically they are created by a master
unit, which is not a coroutine.
For example (from Simula 67),
Parallel Programming
For a parallel process to be initiated, there must be some command to
start a process, whilst executing another. Thoeretically the AND command
does this
In practise languages implement the AND statement in a variety of forms..
C uses fork().. which initiates a copy of the current subprogram.
Ada allows the declarations of tasks inside packages.. Then whenever the
package is included in a program, it automatically starts each task. Ada
also allows the definition of task types then each time a `variable'
is declared, another task is started.
Synchronisation
Parallel processing can give unusual results. Consider
The solution to this problem is to use some form of command to synchronise
the commands. Two major options are used: semaphores and message passing.
Semaphores
A semaphore is a datatype containing both a counter and a process queue. Two
operations are provided, often called WAIT and RELEASE.
For example, suppose there is a shared data structure named STUFF, for which
mutually exclusive access is required by the sharing procedures. This may be
accomplished by a semaphore (called SEMASTUFF below), assuming all procedures
accessing STUFF have the form
The portion of the code that accesses STUFF is known as the critical region;
it is protected by the wait and release calls on SEMASTUFF. The assumption is
that the wait statement completes only when the value of the semaphore counter
is greater than 1, indicating that STUFF is not currently being accessed; if
the counter value is 0, the process is placed in the queue of SEMASTUFF. When
completed the wait statement subtracts one from the counter. The release
procedure increases the value of the counter by 1 thus allowing another
process to access STUFF.
Note that semaphores may be extended to encompass resources which may be used
by a fixed number of processes, by simply allowing the counter to count the
number of available resources remaining.
Semaphores have a number of disadvantages:
Message Passing
The idea behind message passing is that if task A wishes to communicate with
task B, it sends a message. If B is currently busy, A is suspended and placed
in hold until B is available. When B becomes available (called a rendezvous),
A remains suspended until B has completed the processing of the message; at
that point, A is reawakened. Correspondingly, if B reaches a point where it
requires a message, but no appropriate message is available, it is suspended
until a message becomes available.
If more than one task requires access to B, some mechanism for determining
which task gets access next is required. It might be thought that a queue is
the best structure, but in fact this makes deadlocks more likely. It is
preferable to choose amongst the waiting tasks nondeterministically, whilst
providing some mechanism to ensure fairness, which essentially means that all
the waiting tasks have an equal chance of access.
Often the message passing occurs in a guarded command, i.e.
Shared Data
When data is to be shared between processes, there must be some way to
ensure only one process may have access to it at a time. The options include
Critical Regions
The section of code that may may access the data object, may only be executed
by one procedure at a time (often created by the use of pointers).
Monitor
A monitor is a data object which may only be accessed by certain, associated
procedures. Any data object may access the procedures but only one of the
procedures may execute at a given time.
Message Passing
When using message passing each data object is owned by one process, but
may be passed (as a message) to others.
Review
begin
class producer;
begin
integer stuff;
ref (consumer) cons;
detach;
while true do
begin
buf := stuff;
resume(cons);
end
end;
class consumer;
begin
integer value;
ref (producer) prod;
detach;
while true do
begin
value := buf;
resume(prod);
end
end;
integer buf;
ref (producer) prod1;
ref (consumer) cons1;
prod1 :- new producer;
cons1 :- new consumer;
prod1.cons :- cons1;
cons1.prod :- prod1;
resume (prod1)
end;
statements;
statement11 and statement2 and statement3;
statemente;
Once statements was executed, statement1, statement2 and statement3 would
start executing. Once all three had completed execution.. statemente would
start. Note that any of the statements could be a subprogram call.
x:=1;
x:=2 and y:=x+x;
print(y);
The answer printed might be 2, 4 or .. 3!!
process example
begin
.
wait (SEMASTUFF)
-- access STUFF --
release (SEMASTUFF)
.
end
select
when receive(F,G)=> ...
when a>3 and receive(X,T)=>...
end select;
Which says that if at least one of the guard clauses is true, then choose
randomly one of the true clauses and execute it.
Next: About this document
Monday June 15 13:51:40 EST 1998