home *** CD-ROM | disk | FTP | other *** search
-
-
-
- CONCURRENT PROGRAMMING IN TURBO PASCAL
- ======================================
-
-
- Documentation with "CONCUR.PAS" Hans Passant,
- Version 1.00 132 Woodhaven Dr
- Dated : Dec 15th, 1986 Mars, PA 16046
- User ID : 76266,2746
-
-
- Introduction
- ------------
-
- Anyone who ever tried to write industrial control software for
- DOS-computers would probably agree with me how un-suited DOS is
- for such applications. This type of software is in nature multi-
- tasking, usually the program controls several production centers
- that operate independent from one another but do need to share
- large amounts of data. The commercial multi-tasking kernels (like
- DoubleDos and Desqview) cannot be used, they lack the kernel
- support for communication between the tasks. Operating systems
- like Unix and Xenix do have all the features we need but develop-
- ment software is hard to find and very expensive. I therefor
- decided to develop a multi-tasking kernel for Turbo Pascal.
-
- Multi-tasking is a process where several independent pieces of
- code (tasks) compete for execution. Obviously, the CPU can
- execute only one task at a time. The trick is to allow each task
- to execute for a small amount of time, after which the next task
- is executed. If this happens often enough, the computer appears
- to run all of the tasks at the same time : true multi-tasking
- behavior.
-
- The process of suspending the currently active task and
- activating the next is called a "context-switch". All variables
- that are used by the task to be suspended (the task-context) must
- be saved so the task can be reactivated and continue where it
- left off when the task is again eligible for execution. Generally
- the task-context is made up of the current processor registers
- and the stack. Saving all the registers and preserving the task
- stack is therefor enough.
-
- Turbo Pascal is perfectly suited for this type of context-switch.
- It saves all of its procedure parameters and local variables on
- the stack. The context of each task can therefor be preserved by
- allocating a stack for each individual task. The task-context
- switch procedure then only has to save the processor registers on
- the task-stack, set the stackpointer to the task stack for the
- next task to execute and restore the registers for that task.
- There is however a catch, some variables used in the Turbo Pascal
- library belong to the task-context (Example : the IoError-
- variable). Also, the programmer may introduce some global
- variables that are used by more than one task. The kernel takes
- care for the shared library variables. It saves them on the task
- stack upon a context-switch. The programmer is responsible for
-
-
- 1
-
-
-
- protecting the shared use of any global variables. An example is
- given in the kernel-code.
-
- Left to discuss is the mechanism that forces a context switch.
- True multi-tasking operating systems always use a hardware
- interrupt, triggered by a timer. The mechanism used in CONCUR.PAS
- is a simple coded call to the context-switch procedure. This
- method is known as "concurrent programming" and is popularized by
- MODULA-2 and "Concurrent Pascal". The disadvantage of the latter
- mechanism is that a context switch does not occur until
- explicitly called for. A badly coded task may contain a long or
- infinite loop without a call to the context switch procedure. The
- task continues to execute without any concurrent tasks being
- executed.
-
- The big advantage of concurrent programming however is the sim-
- plification of the kernel. A hardware triggered context switch
- may suspend a task that is executing a library routine. The Turbo
- Pascal library is non-reentrant (for the same reasons DOS is non-
- reentrant). The kernel would therefor have to save all library
- data structures on a context switch, a very time-consuming opera-
- tion.
-
-
-
- Tasks
- -----
-
- Tasks in CONCUR are simple, parameterless Turbo procedures. Each
- task has a tasknumber and a private area in the Turbo stack
- segment for its stack : the task stack. A program always contains
- one task : the main program. By convention it has task number 0.
- Other tasks are eligible for execution by installing them. The
- install procedure claims an area of memory for the task stack and
- creates a stack frame for the task. Once a task is installed it
- can never end execution.
-
- The task install procedure needs the start address for the task
- (obtained by <ofs (task)>), the required stack size (in para-
- graphs) and the task number. The amount of stack needed for a
- task depends on the size of its local variables and the recursion
- depth (if any). Failing to assign enough stack to a task may
- cause erratic program behavior at worst or a "stack overflow" or
- "heap/stack" collision run time error at best.
-
-
-
-
-
-
-
-
-
-
-
-
-
- 2
-
-
-
- The task stacks
- ---------------
-
- Each task participating in the concurrent programming scheme is
- assigned a reserved area in the Turbo stack segment for its own
- stack. This area should be reserved at start-up of the program.
- The procedure <taskinit> does just that. It should be called with
- a parameter set to the total amount of paragraphs required by the
- sum of the task stacks. The stack area is reserved by moving the
- Turbo stack down in memory. The variable <taskarea> points to the
- memory obtained by the procedure call. <taskareasize> holds the
- size of the area.
-
- A subsequent call to the procedure <installtask> takes a chunk
- from the taskarea and assigns it to the task being installed. A
- stack pointer is created, pointing into the task stack, and is
- saved into the stackpointer table <sptable>. The task stack is
- initialized with the register contents for the task. The task is
- now ready to run, activated by a call to <switchtask>.
-
-
-
- Task switching
- --------------
-
- The concurrent programming method hinges around the procedure
- <switchtask>. This procedure is responsible for the context
- switch needed to deactivate the current task and activate the
- next. Switchtask takes the following steps :
-
- 1. Saves all data registers on the stack.
- 2. Checks the stack pointer for any stack overflow.
- 3. Saves the stack pointer (SS:SP) in the stack table <spsave>.
- 4. Scans <activetasks> for the next installed task.
- 5. Retrieves the stack pointer for that task from <spsave>.
- 6. Assigns <currenttask> the task number of the new task.
- 7. Restores all data registers from the new task's stack.
- 8. Continues execution in the new task.
-
- Task switching does not occur until <switchtask> is called. This
- restriction makes the distinction between concurrent programming
- and multi-tasking. A multi-tasking system initiates task swit-
- ching independent from whatever task is running, usually trig-
- gered by a clock interrupt. This means that when your program
- should behave like a true multi-tasking program, the tasks should
- call <switchtask> frequently and should avoid lengthy execution.
-
-
-
-
-
-
-
-
-
-
-
- 3
-
-
-
- Sharing input
- -------------
-
- The above mentioned restrictions imposed by the concurrent
- programming model are most severe when reading from the keyboard.
- Whenever a <read (input, ....)> is coded, Turbo Pascal transfers
- control to a library procedure that accepts keyboard input and
- only returns when "Enter" is pressed. While this is happening, no
- calls to <switchtask> are made : the entire program "hangs" until
- input is ready.
-
- CONCUR.PAS contains a replacement "CON" input driver that avoids
- this behavior : <conin>. <Conin> checks the keyboard for any key
- pressed. If none is pressed, it saves the library variables that
- are shared with other library routines on the stack and calls
- <switchtask> until a key is pressed. Other tasks may continue to
- run.
-
- The keyboard must be shared when two or more tasks need input.
- The procedures <claiminput> and <releaseinput> takes care for
- that. A task needing input calls <claiminput>. The flag
- <inputbusy> is set. The task now gets control over <input>. As
- soon as input is completed, <releaseinput> is called, releasing
- the keyboard for other tasks.
-
- Another task needing input should call <claiminput> too. It finds
- <inputbusy> set. <Switchtask> is called until the first task
- claiming <input> releases the keyboard, allowing the second task
- to take control over the keyboard.
-
-
- Sharing output
- --------------
-
- The above mentioned sharing problems also exist for output to the
- screen. CONCUR.PAS therefor contains a replacement screen output
- driver <conout>. Each concurrent task has its own window on the
- screen. Initially, each task gets a window of full screen size.
- Upon activation, the task should call the procedure <window> to
- shrink its window to a part of the screen, reserved for that
- task. The procedure <border> optionally draws a border around
- that window.
-
- CONCUR.PAS contains a replacement for all screen associated
- standard Turbo Pascal procedures. These procedures are now just
- effective for the window of the task that calls the procedure.
- Example : <clrscr> now just clears the window of the task and
- leaves the rest of the screen untouched. A list of procedures
- effected follows :
-
- - Window : sets up a window for the current task.
- - InsLine/DelLine. Effective in the task window only.
- - ClrEol/ClrScr. Effective in the task window only.
- - GotoXY. Uses the screen coordinates in the task window.
- - WhereX/WhereY. Returns coordinates from the task window.
-
-
- 4
-
-
-
- - TextColor/TextBackground. Changes colors in the task window
- only.
- - LowVideo/NormVideo. As above.
-
- Procedure <clearscreen> has been redefined to clear the entire
- screen, regardless of the task calling the procedure.
-
-
-
- Procedure entries
- -----------------
-
- The following procedures in CONCUR.PAS may be called :
-
- - TaskInit (stack_request : integer). This procedure MUST be
- called at program start-up. It initializes all data
- structures and module parts. The procedure must be called
- with one parameter : the sum of all stack sizes for every
- task, expressed in paragraphs (16 bytes).
-
- - InstallTask (address, stack_size, task_number : integer). This
- procedure should be called to install a task. <Address> is
- the start address of the task and is obtained by "ofs
- (task)". <Stack_size> is the amount of stack needed by the
- task (in paragraphs). <Task_number> is the task number for
- the installed task and should range from 1 to 15.
-
- - SwitchTask. This procedure does the context switch from the
- currently active task to the next installed task.
-
- - Window (x1,y1,x2,y2 : integer). This procedure should be called
- by each task (including task 0, the main program !!) to
- reserve a portion of the screen for its output. <x1, y1> are
- the screen coordinates for the upper left corner, <x2, y2>
- is the lower right corner.
-
- - Border (name). Draws a border around the window of the current
- task. <name> is shown on top of the window.
-
- - ClearScreen. May be called to clear the entire screen.
-
-
- Program design
- --------------
-
- The following basic steps should be made when designing a program
- using the concurrent programming method :
-
- 1. Make a logical division in your program into actions that
- should occur concurrently. This will produce the tasks.
- Remember that the main program always acts as one of the
- tasks, you may want to use it to monitor the execution of
- the entire program. Avoid having too many tasks, the more
- tasks are present, the slower each of them will run.
-
-
-
- 5
-
-
-
-
- 2. Choose a screen layout for the windows of each task producing
- screen output. If you have a color monitor, be liberal with
- the use of TextColor and TextBackground. Have the tasks call
- <window>/<border> as soon as they start execution.
-
- 3. Write the code for each concurrent task. Decide where calls to
- <switchtask> should be made. Each concurrent task should at
- least contain an infinite loop, so a call should be made
- inside that loop. Inspect other loops and place calls at the
- deepest nesting levels.
-
- 4. Include the include-files :
- 1. {$i biosdec.pas} {BIOS-call declarations}
- 2. {$i libdec.pas} {Turbo library declarations}
- 3. {$i concur.pas} {Concurrent programming kernel}
-
- 5. Inspect your code for tasks that need input from the keyboard.
- If at least two tasks need input, surround the input code
- with calls to <claiminput> and <releaseinput>.
-
- 6. Inspect your code for shared access to global variables, files
- and devices. Access may need to be controlled through an
- access flag. Example :
-
- procedure claimvar; procedure task1;
- begin .....
- while var_in_use do switchtask; claimvar;
- var_in_use := true; ....
- end; releasevar;
-
- procedure releasevar; procedure task2;
- begin ....
- var_in_use := false; claimvar;
- end; ....
- releasevar;
-
-
- A prototype task would be coded as follows :
-
- procedure task1;
- begin
- textbackground (..); {Background color for task window}
- window (....); {Create task window}
- textcolor (..); {Color for border}
- border (....); {Create border around window}
- textcolor (..); {Color for text in window}
- repeat
- ....
- switchtask; {At least one call to switchtask}
- until false; {Task should contain infinite loop}
- end;
-
-
-
-
-
- 6
-
-
-
- Installing CONCUR
- -----------------
-
- CONCUR is highly compiler version specific, due to the use of
- several addresses in the code and data segments of the Turbo
- Pascal library. You will find the declarations of these locations
- in the include file "LIBDEC.PAS". The declared values were
- obtained with version 3.01A, emulating real math. To check
- whether your compiler can work with these values, compile and run
- the program "VERSION.PAS". It will tell you want changes must be
- made for your compiler. If the message "Cannot find address for
-
-
-
- Restrictions
- ------------
-
- 1. Do not use the $P and $G compiler switches. They will
- circumvent calls to the replacement <conin> and <conout>
- drivers.
- 2. Control-S/Q will no longer work during screen output, due to
- the replacement <conout> driver.
- 3. Access to global variables by more than one task is dangerous.
- A sharing protocol is needed, similar to the <inputbusy>
- method employed by the input driver.
- 4. A maximum of 15 tasks may be installed.
- 5. If you have a 8088 CPU based system (the IBM PC and XT models)
- some changes have to be made in the <switchtask> procedure.
- See code for details.
- 6. The installed tasks MUST be parameterless !!!
- 7. Be very careful when using overlays in your code. An overlay
- procedure that is overlayed with a procedure used by another
- task should never call <switchtask>.
-
-
- Problems and error messages
- ---------------------------
-
- The following error messages may appear, they will abort the
- program :
-
- - "Stack overflow in task ..". You did not reserve enough stack
- space when calling <installtask>. Increase the number.
- - "Unexpected abortion of task ..". Installed tasks are not
- allowed to abort. Revise your code and make sure a task
- contains an infinite loop.
- - "Illegal task number ..". Task numbers should be in (1..15).
- - "Not enough stack to install task ..". A call to <installtask>
- requested more stack space than was allocated by <taskinit>.
- - "Double installation of task ..". Task numbers should be
- unique.
-
- Failing to request enough stack space for a task may result in
- erratic behavior of the program. The stack of another task may be
- destroyed without Turbo or CONCUR detecting the error. Make sure
-
-
- 7
-
-
-
- enough stack space is reserved for each task. As a rule of thumb,
- add the size of all local variables and divide by 16 to get the
- number of paragraphs. Add 20 for safety. If the task does any
- recursion, add plenty more (depending on the maximum recursion
- depth).
-
- A stack overflow occasionally may be detected by the run time
- code and will result in Turbo run time error message <FF>
- (heap/stack collision) or the message "Stack overflow in task".
-
-
- Files in CONCUR.ARC
- -------------------
-
- 1. CONCUR.PAS : the concurrent programming module.
- 2. BIOSDEC.PAS : bios call declarations. Should be included by
- any program before including CONCUR.PAS.
- 3. LIBDEC.PAS : declarations for variables in the library.
- 4. CPDEMO.PAS : a simple demonstration program using CONCUR. To
- abort the program, press any key and type "halt <enter>".
- 5. CONCUR.DOC : this file.
- 6. VERSION.PAS : compile and run this program with your own
- Turbo Pascal compiler. It will tell you if any changes have
- to be made in LIBDEC.PAS.
-
- Future
- ------
-
- I am currently working on a true multi-tasking version of CONCUR.
- Expect versions of MULTI.ARC Real Soon Now.
-
- I am looking forward to your experience with CONCUR.PAS. Please
- let me now if you find any serious errors, particularly the ones
- that will hang up your computer. My user ID on the COMPUSERVE
- network is [76266,2746].
-
-
- - Hans Passant -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 8