home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
PASCAL
/
MULTI12
/
MULTI.DOC
< prev
next >
Wrap
Text File
|
1989-06-04
|
27KB
|
669 lines
1. Introduction
---------------
The following is a description of the MULTI unit for Turbo Pascal 4.0 or
later. The unit will allow you to run several TP-procedures as concurrent
processes. It also provides facilities for synchronizing the processes and for
communication between them.
The parallellism implied is not strictly true: your computer (presumably)
has only one processor and what the unit does is to share that processors time
between the various processes to create an illusion of parallel processing.
This assembly of concurrently executing procedures is called a multiprogram.
The unit will run on any IBM PC or compatible computer with Turbo Pascal
version 4.0 or 5.0. You may need to change some interrupt numbers, see
section 8.
This file should be a part of a package containing the following files:
MULTI.PAS - Source file for the unit
MULTI.TPU - TP5 compiled version of the unit
MULTI.DOC - This file - documentation of the unit
MUL_DEMO.PAS - Demo program - source file
MUL_DEMO.EXE - Executable version of demo program
JABWOCK.DAT - Data file used by demo program
2. Managing Processes
---------------------
The unit will allow you to specify that several of your programs procedures
are to be executed in parallel. You can also execute several copies of the
same procedure. The unit contains the follwing procedures for managing
processes:
Procedure CreateProcess Adds a new process to the multiprogram.
Procedure Kill Kills the specified process, i.e. removes
it from the multiprogram.
Procedure Die Causes a process to commit suicide, i.e.
it removes itself from the multiprogram.
Function NoProcesses Returns the number of processes currently in
the multiprogram.
3. Managing the multiprogram
----------------------------
There are two operations you can perform on the multiprogram as a whole:
you can start it, and you can stop it. When you start your program it will
execute as a normal, sequential program. You then define some processes using
CreateProcess. The processes start executing when you start the multiprogram,
and stops when you stop the multiprogram. The relevant procedures are:
Procedure StartMulti Starts the multiprogram, commencing
execution of the processes.
Procedure StopMulti Stops the multiprogram, returns control
to the normal, sequential program.
4. Synchronizing processes
--------------------------
Normally, you want to be able to coordinate what you processes do to
avoid conflict between them. This is done by means of semaphores. They
are used for allocating nonshareble resources such as the disk or the
keyboard. The semaphores in the multi unit are integer semaphores consisting
of a counter and a queue. The two basic operations on these semaphores are
wait and signal defined as follows:
wait : Begin
While counter<=0 Do sleep;
counter:=counter-1;
End;
signal : Begin
counter:=counter+1;
If any processes are waiting Then wake one up;
End;
Also, you must initialize the semaphore before using it giving a total of
three semaphore operations:
Procedure Wait Performs wait-operation on semaphore.
Procedure Signal Performs signal-operation on semaphore.
Procedure InitSem Initializes semaphore.
5. Communication between processes
----------------------------------
There are two methods for communicating between processes: shared data and
message passing. The data structures declared in the main program reside in
the data segment and are common to all processes. However, there is no
proctection against simultanious acces by several processes, so you will
have to guard your shared data with sempahores to prevent this.
The other method, message passing, is implemented using message semaphores.
A message semaphore is a billboard where a process can pin messages. Another
process can then read the message and take appropriate action according to
the content of the message. A message is a data structure which can have any
form. The only constraint is that the unit uses the first 8 bytes (two
pointers) for internal administration. A message could for example be declared
like this:
message = Record
dummy1, { MUST start with 8 bytes of reserved }
dummy2 : Pointer; { space. }
YourData : string; { You can have any number of fields
End; { here of any type you like }
There are three operations you can perform on a message semaphore:
Procedure PutMsg Sends a message to the specified message
semaphore.
Procedure GetMsg Reads a message from the specified message
semaphore.
Procedure InitMsgSem Initializes the specified message semaphore.
6. Restrictions
---------------
When using the multi unit you must beware of the following restrictions.
a) The unit will not support numeric coprocessors. The multiprogram will
run on machines with coprocessors but only one process may use it or
you will get unpredictable results.
b) MSDOS is not reentrant. This meens that only one process at a time can
use dos functions such as reading from the keyboard, writing to disk
etc. This shortcomming can be remedied by guarding dos calls by a
semaphore. You may try to experiment with having, say, a process writing
to disk while another reads characters from the keyboard but this is
skating on thin ice. You may examine the accompanying demonstration
program for an example of how to use dos functions.
c) Some Turbo Pascal routines are not reentrant. This is true of the heap
manager and the screen output routines. Thus, only one process at a
time should allocate or release space the heap and write to the screen.
d) The Delay procedure will not work properly when running a multiprogram.
For some reason, using this procedure will crash the system.
e) It is an error for a process to terminate 'normally', i.e. to reach
the final End-statement in the procedure body. Should this happen,
the system will hang. To prevent this you should always place a call
to procedure Die immediately before the final End-statement in all
processes.
7. Programming considerations
-----------------------------
Multiprograms are notoriously hard to debug so you will have to be extra
carefull in designing your program. First of all you must ask yourself if you
really need to write your application as a multiprogram. A multiprogram is
not as efficient as a sequential program because some processing power is lost
to administration of the processes, semaphores etc. Multiprograms are well
suited to handling input form various sources at the same time, so
communications programs are good candidates for multiprograms, as are other
programs which interact heavily with the user. Other possible applications are
database programs (you can do sorts, print lists, execute searches in the
database etc as seperate background processes) and word processors (you can
print or reformat text in the background).
If possible, debug your processes one at a time as sequential programs. When
the multiprogram chrashes, it really CRASHES, often to the point where you
have to switch off your machine to make it work properly. So, lets be carefull
out there.
There are two factors which will probably limit you in your multiprogram
design. One is the amount of ememy available, the other is the fixed amount
of processor capacity in your system.
When considering the memory limit you should remember that the processes get
their stack space from the heap. You should therefore try to determine the
memory needs of each process when you create it. Remember that the stack space
is used to hold local variables and call references for procedure calls from
the process. You should be prudent but not mean when allocating stack space
for the processes. You may, however, cut down the stack space for the main
sequential program. This space is only used for call references in the main
program and for a typical multiprogram this will just consist of setting up
the processes and initializing semaphores and other data structures. You can
adjust the amount of stack space allocated to the main program by using the
$M compiler directive.
If you have a 80286 or 80386 based computer, you should not have any
problems with the capacity of your processor. On a plain vanilla PC you risk
run into trouble with response time even with a relatively modest number of
processes. You can estimate the size of this problem by running the
demonstration program which runs 8 processes in parallel.
8. Defined types and constants
------------------------------
The interface section of the unit contains the following declarations:
unit multi;
interface
uses Dos;
const TimerIntNo = 8; (* Interruptno. for timer *)
SavedInt = 78; (* Int.no. for rerouting timer *)
Interleave = 1; (* Interleave factor for timeslicing *)
type ProcedureType = procedure;
queuetype = ^ProcDescriptor;
process = queuetype;
ProcDescriptor = Record
Next : queuetype;
Inqueue : ^queuetype;
Sseg,
SP,
ProcStack : Word;
ptr : Pointer;
End;
semaphore = Record
queue : queuetype;
counter : Integer;
End;
Msgtype = pointer;
MsgSemaphore = Record
ProcQueue,
MsgQueue : queuetype;
End;
GetMode = (stay, return);
SwitchMode = (Timer, NoTimer);
The constant TimerIntNo specifies the interrupt number of DOS' time function
which triggers 20 times pr. second. If this is different on your machine, you
must change this constant appropriately.
The constant SavedInt specifies an interrupt used by the multi unit. If you
find that the unit does not work with your favorite resident program (SideKick
and the like), you may solve the problem by changing this number.
The constant Interleave specifies the longest timeslice a process can have in
multiples of 50 ms. If you have a multiprogram with relatively little
communication between processes you may improve its performance by increasing
the value of this constant.
9. Multi reference
------------------
This section provides a reference guide to the procedures and functions
of the multi unit in alfabetical order.
------------------------------------------------------------------------------
CreateProcess Procedure Process management
------------------------------------------------------------------------------
Function Adds a new process to the multiprogram.
Declaration Procedure CreateProcess(body: Pointer; StackSize: word;
var proc: process);
Remarks The parameter body should contain the address of the procedure
to be added as a process to the multiprogram, for example
@MyProcess. The parameter StackSize determines the amount of
memory reserved for local variable and further procedure calls
in the process. The memory is taken from the heap and you may
allocate 64..65521 bytes. If you allocate less then 1024 bytes
you should compile your program with the {$S-} directive.
Otherwise Turbo Pascal will generate a stack overflow error.
The procedure will return pointer to a process descriptor
in the parameter proc. This desciptor is used when referring to
the process in the kill procedure.
Example Program count;
var proc1,proc2 : Process;
Procedure MyProcess;
var i : Integer;
Begin
For i:=1 to Maxint Do
Writeln(i);
Die;
End;
Begin { main }
CreateProcess(@MyProcess,2000,proc1);
CreateProcess(@MyProcess,2000,proc2);
StartMulti;
End.
Restrictions You may add processes as long as there is sufficient space
on the heap. A runtime error is generated if this is not the
case.
See also Kill, Die, NoProcesses
------------------------------------------------------------------------------
Die Procedure Process Management
------------------------------------------------------------------------------
Function Causes a process to stop itself.
Declaration Procedure Die;
Remarks The stackspace reserved for the process is released and
control is transferred to one of the remaining processes. If
no processes remain, the multiprogram is stopped and execution
resumes in the main program at the statement following the
call of StartMulti.
It is an error for processes to terminate 'normally', i.e. to
reach the end-statement in the procedure body. Therefore, all
processes should contain a call to Die immediately before the
final end-statement.
Example Procedure MyProcess;
Begin
Writeln('Do something to show life....');
Die;
End;
Restrictions None.
See also Kill, CreateProcess, NoProcesses
------------------------------------------------------------------------------
GetMsg Procedure Communication
------------------------------------------------------------------------------
Function Reads a message from the message semaphore MsgSem.
Declaration Procedure GetMsg(var MsgSem: MsgSemaphore; var Msg : Pointer;
mode: Getmode);
Remarks A pointer to the message is returned in Msg. You can choose
between two modes of getting: if the parameter mode=stay, the
process will wait until a message arrives at the message
semaphore. If mode=return, if will return immediately and if
there was no message Msg will be nil.
Example type MyMessageType = record
dummy1,
dummy2 : Pointer;
news : string;
End;
Procedure Reader;
var ptr : pointer;
message : ^MyMessageType;
Begin
Repeat
GetMsg(billboard,ptr,stay);
message:=ptr;
Writeln('News: ',message^.news);
Until
false;
End;
Restrictions The message semaphore must be intialized before use.
See also PutMsg, InitMsgSem
------------------------------------------------------------------------------
InitMsgSem Procedure Communication
------------------------------------------------------------------------------
Function Initializes the message semaphore.
Declaration Procedure InitMsgSem(var MsgSem : MsgSemaphore);
Remarks MUST be called before the message semaphore is used.
Restrictions May NOT be called when the semaphore is in use.
See also PutMsg, GetMsg
------------------------------------------------------------------------------
InitSem Procedure Synchronization
------------------------------------------------------------------------------
Function Initializes the semaphore setting the counter to 0.
Declaration Procedure InitSem(var sem : semaphore);
Remarks MUST be called before semaphore is used. If you want the counter
of the semaphore set to some value other than 0 you can do this
by repeated calls to Signal which will increment counter. If you
need to set counter to some monstrously large value you can set
it directly by sem.counter:=whatever. However this is not
recommended.
Restrictions may NOT be called when the semaphore is in use.
See also Signal, Wait
------------------------------------------------------------------------------
Kill Procedure Process Management
------------------------------------------------------------------------------
Function Stops the specified process and removes it from the multi-
program.
Declaration Procedure Kill(proc: process);
Remarks The stackspace reserved for the killed process is released.
Example Procedure spawn;
var child : process;
Begin
{ Make clone of process }
CreateProcess(@spawn,2000,child);
{ Wait a little while }
Delay(500);
{ Then kill it again }
Kill(child);
End;
Restrictions Kill will kill anything and anybody. No process is safe from
being killed by another. So use with care.
See also CreateProcess, Die, NoProcesses
------------------------------------------------------------------------------
NoProcesses Function Process Management
------------------------------------------------------------------------------
Function Returns the number of processes currently in the system.
Declaration Function NoProcesses : Integer;
Example Procedure monitor;
Begin
Repeat
Writeln(NoProcesses);
Until
false;
End;
Restrictions None.
See also CreateProcess, Die, Kill
------------------------------------------------------------------------------
PutMsg Procedure Communication
------------------------------------------------------------------------------
Function Sends a message to a message semaphore.
Declaration Procedure PutMsg(var MsgSem : MsgSemaphore; Msg : Pointer);
Remarks The message is sent to the message semaphore MsgSem. The parameter
Msg should containa pointer to the message begin sent.
Example type MyMessageType = record
dummy1,
dummy2 : Pointer;
news : string;
End;
Procedure talk;
var message : ^MyMessageType;
Begin
new(message);
message^.news:='Yack Yack Yack');
PutMsg(MsgSem,pointer(message));
Die;
End;
Restrictions The message semaphore must be initialized.
See also GetMsg, InitMsgSem
------------------------------------------------------------------------------
Signal Procedure Synchronization
------------------------------------------------------------------------------
Function Performs the signal operation on the semaphore.
Declaration Procedure Signal(var sem: semaphore);
Remarks The semaphores counter is incemented. If any processes are
waiting for this to happen, one of them is awakened.
Restrictions Semaphore must be initialized.
See also InitSem, Wait
------------------------------------------------------------------------------
StartMulti Procedure MultiProgram Management
------------------------------------------------------------------------------
Function Starts execution of the defined processes in the multiprogram.
Declaration Procedure StartMulti(mode : SwitchMode);
Remarks The paramater mode indicates whether processes should be timed
out using the 50 ms timer interrupt. If mode=Timer, timeout is
enabled, if mode=NoTimer it is disabled. When debugging a
multiprogram it is preferable to use NoTimer as this will allow
you to use Turbo Pascal's debugger. When mode=timer a program
crash usually means that you have yo reset your system.
However, when mode=notimer, the multiprogram depends on the
processes voluntarily giving up control of the system. This
happens when a process calls Signal or PutMsg. If a process
contains a read-statement or an infinite loop the entire system
will hang.
Restrictions It is an error to call StartMulti before any processes have
been declared. Doing so will hang the system.
See also StopMulti
------------------------------------------------------------------------------
StopMulti Procedure MultiProgram Management
------------------------------------------------------------------------------
Function Stops the multiprogram.
Declaration Procedure StopMulti;
Remarks When one of the processes calls this procedure or when all
processes are dead, execution of the multiprogram stops and
control is returned to the main program. Execution of the
main program resumes at the statement immediately following
the call to StartMulti.
Restrictions None.
See also StartMulti
------------------------------------------------------------------------------
Wait Procedure Synchronization
------------------------------------------------------------------------------
Function Performs the wait operation on a semaphore.
Declaration Procedure Wait(var sem: semaphore);
Remarks Decrements the counter of the semaphore. If the counter was 0
when the procedure was called the process will sleep until a
call to signal by another process awakes it.
Example var ScreenSem : semaphore;
proc1,
proc2 : Process;
Procedure print;
Begin
For i:=1 to 30000 Do
Begin
wait(ScreenSem); { Ensure undisturbed acces to screen }
Writeln(i);
signal(ScreenSem);
End;
Die;
End;
Begin { Main }
CreateProcess(@print,2000,proc1);
CreateProcess(@Print,2000,proc2);
InitSem(ScreenSem);
Signal(ScreenSem);
StartMulti;
End.
Restrictions Semaphore must be initialized.
See also InitSem, Signal
10. Inside the unit
-------------------
The unit administrates the processes by placing them in various queues.
The status of the process is determined by the queue it is placed in.
There is a queue containing the process currently running (CurrentProc), a
queue for processes that are ready to run but await their turn (Readyqueue),
a queue for processes that have been killed and are waiting to be removed
form the system (Dead). All semaphores and message semaphores contain queues
for the processes that may have to wait for them.
The queues are implemented as single-linked circular lists. All list
opearations are done by the procedures Enqueue and Dequeue, so if you want
to change the list implementation, all you have to do is to change these two
procedures.
The unit shares the processor time equally between the processes in the
readyqueue in a round-robin fashion. There are no priorities in the system,
but you can implement that yourself if you like.
What really is contained in the queues are the proces descriptors which
contain information about the state of each process. Specifically they
store the stack segment and stack pointer of each process, along with a
pointer to the head of the queue in which the process is contained. Switching
process then simply means switching the value of the stack pointer SSeg:Sp.
This is done in the procedure SwitchContext and requires a bit of inline
assembler code.
11. Version history
-------------------
This is version 1.2 of the MULTI unit. The history of the unit is as
follows:
V 1.0 : Was the first version released.
V 1.1 : Was the second version released. It contained no changes in the
definitions of the various procedures and funtcions, but corrected
a few spelling errors in the documentation and the demonstration
program.
V 1.2 : Is this version, the third to be released. In this version a serious
error was corrected: the previous versions did not run under TP4. This
has now been corrected. The error was caused by a change in the way
TP lays out its variables from TP4 to TP5, which made procedure
SwitchContext crash (may Anders Hejlsberg's beard get stuck in a
faulty disk-drive).
Also, Yet Another Batch Of Typing Errors was corrected.