home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / CONCUR.ZIP / CONCUR.DOC < prev    next >
Encoding:
Text File  |  1987-03-16  |  20.9 KB  |  489 lines

  1.  
  2.  
  3.  
  4.         CONCURRENT PROGRAMMING IN TURBO PASCAL     
  5.         ======================================
  6.  
  7.  
  8.         Documentation with "CONCUR.PAS"         Hans Passant, 
  9.         Version 1.00                            132 Woodhaven Dr
  10.         Dated : Dec 15th, 1986                  Mars, PA 16046
  11.                                                 User ID : 76266,2746
  12.  
  13.  
  14.         Introduction
  15.         ------------
  16.  
  17.         Anyone  who  ever tried to write industrial control software  for 
  18.         DOS-computers  would probably agree with me how un-suited DOS  is 
  19.         for such applications.  This type of software is in nature multi-
  20.         tasking,  usually the program controls several production centers 
  21.         that  operate  independent from one another but do need to  share 
  22.         large amounts of data. The commercial multi-tasking kernels (like 
  23.         DoubleDos  and  Desqview) cannot be used,  they lack  the  kernel 
  24.         support  for communication between the tasks.  Operating  systems 
  25.         like Unix and Xenix do have all the features we need but develop-
  26.         ment  software  is hard to find and very  expensive.  I  therefor 
  27.         decided to develop a multi-tasking kernel for Turbo Pascal. 
  28.  
  29.         Multi-tasking  is a process where several independent  pieces  of 
  30.         code  (tasks)  compete  for execution.  Obviously,  the  CPU  can 
  31.         execute only one task at a time.  The trick is to allow each task 
  32.         to execute for a small amount of time,  after which the next task 
  33.         is executed.  If this happens often enough,  the computer appears 
  34.         to  run all of the tasks at the same time  :  true  multi-tasking 
  35.         behavior. 
  36.  
  37.         The   process  of  suspending  the  currently  active  task   and 
  38.         activating  the next is called a "context-switch".  All variables 
  39.         that are used by the task to be suspended (the task-context) must 
  40.         be  saved  so the task can be reactivated and continue  where  it 
  41.         left off when the task is again eligible for execution. Generally 
  42.         the  task-context  is made up of the current processor  registers 
  43.         and the stack.  Saving all the registers and preserving the  task 
  44.         stack  is therefor enough.  
  45.  
  46.         Turbo Pascal is perfectly suited for this type of context-switch. 
  47.         It  saves all of its procedure parameters and local variables  on 
  48.         the stack.  The context of each task can therefor be preserved by 
  49.         allocating  a  stack for each individual task.  The  task-context 
  50.         switch procedure then only has to save the processor registers on 
  51.         the  task-stack,  set the stackpointer to the task stack for  the 
  52.         next  task  to execute and restore the registers for  that  task. 
  53.         There is however a catch, some variables used in the Turbo Pascal 
  54.         library  belong  to  the task-context  (Example  :  the  IoError-
  55.         variable).   Also,  the  programmer  may  introduce  some  global 
  56.         variables  that are used by more than one task.  The kernel takes 
  57.         care for the shared library variables.  It saves them on the task 
  58.         stack  upon a context-switch.  The programmer is responsible  for 
  59.  
  60.  
  61.                                         1
  62.  
  63.  
  64.  
  65.         protecting the shared use of any global variables.  An example is 
  66.         given in the kernel-code. 
  67.  
  68.         Left  to discuss is the mechanism that forces a  context  switch. 
  69.         True  multi-tasking  operating  systems  always  use  a  hardware 
  70.         interrupt, triggered by a timer. The mechanism used in CONCUR.PAS 
  71.         is  a  simple coded call to the  context-switch  procedure.  This 
  72.         method is known as "concurrent programming" and is popularized by 
  73.         MODULA-2 and "Concurrent Pascal".  The disadvantage of the latter 
  74.         mechanism   is  that  a  context  switch  does  not  occur  until 
  75.         explicitly  called for.  A badly coded task may contain a long or 
  76.         infinite loop without a call to the context switch procedure. The 
  77.         task  continues  to execute without any  concurrent  tasks  being 
  78.         executed. 
  79.  
  80.         The  big advantage of concurrent programming however is the  sim-
  81.         plification  of the kernel.  A hardware triggered context  switch 
  82.         may suspend a task that is executing a library routine. The Turbo 
  83.         Pascal library is non-reentrant (for the same reasons DOS is non-
  84.         reentrant).  The  kernel would therefor have to save all  library 
  85.         data structures on a context switch, a very time-consuming opera-
  86.         tion. 
  87.  
  88.          
  89.  
  90.         Tasks
  91.         -----
  92.  
  93.         Tasks in CONCUR are simple,  parameterless Turbo procedures. Each 
  94.         task  has  a  tasknumber and a private area in  the  Turbo  stack 
  95.         segment for its stack : the task stack. A program always contains 
  96.         one task :  the main program. By convention it has task number 0. 
  97.         Other  tasks are eligible for execution by installing  them.  The 
  98.         install procedure claims an area of memory for the task stack and 
  99.         creates  a stack frame for the task.  Once a task is installed it 
  100.         can never end execution.  
  101.  
  102.         The  task install procedure needs the start address for the  task 
  103.         (obtained  by <ofs (task)>),  the required stack size  (in  para-
  104.         graphs)  and  the task number.  The amount of stack needed for  a 
  105.         task depends on the size of its local variables and the recursion 
  106.         depth  (if  any).  Failing to assign enough stack to a  task  may 
  107.         cause erratic program behavior at worst or a "stack overflow"  or 
  108.         "heap/stack" collision run time error at best.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.                                         2
  123.  
  124.  
  125.  
  126.         The task stacks
  127.         ---------------
  128.  
  129.         Each  task participating in the concurrent programming scheme  is 
  130.         assigned  a reserved area in the Turbo stack segment for its  own 
  131.         stack.  This area should be reserved at start-up of the  program. 
  132.         The procedure <taskinit> does just that. It should be called with 
  133.         a parameter set to the total amount of paragraphs required by the 
  134.         sum of the task stacks.  The stack area is reserved by moving the 
  135.         Turbo stack down in memory. The variable <taskarea> points to the 
  136.         memory  obtained by the procedure call.  <taskareasize> holds the 
  137.         size of the area.
  138.  
  139.         A  subsequent  call to the procedure <installtask> takes a  chunk 
  140.         from the taskarea and assigns it to the task being  installed.  A 
  141.         stack  pointer is created,  pointing into the task stack,  and is 
  142.         saved  into the stackpointer table <sptable>.  The task stack  is 
  143.         initialized with the register contents for the task.  The task is 
  144.         now ready to run, activated by a call to <switchtask>.
  145.  
  146.  
  147.  
  148.         Task switching
  149.         --------------
  150.  
  151.         The  concurrent  programming method hinges around  the  procedure 
  152.         <switchtask>.  This  procedure  is responsible  for  the  context 
  153.         switch  needed  to deactivate the current task and  activate  the 
  154.         next. Switchtask takes the following steps :
  155.  
  156.         1. Saves all data registers on the stack.
  157.         2. Checks the stack pointer for any stack overflow.
  158.         3. Saves the stack pointer (SS:SP) in the stack table <spsave>.
  159.         4. Scans <activetasks> for the next installed task.
  160.         5. Retrieves the stack pointer for that task from <spsave>.
  161.         6. Assigns <currenttask> the task number of the new task.
  162.         7. Restores all data registers from the new task's stack.
  163.         8. Continues execution in the new task.
  164.  
  165.         Task switching does not occur until <switchtask> is called.  This 
  166.         restriction  makes the distinction between concurrent programming 
  167.         and  multi-tasking.  A multi-tasking system initiates task  swit-
  168.         ching  independent from whatever task is running,  usually  trig-
  169.         gered  by a clock interrupt.  This means that when  your  program 
  170.         should behave like a true multi-tasking program, the tasks should 
  171.         call <switchtask> frequently and should avoid lengthy execution.
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.                                         3
  184.  
  185.  
  186.  
  187.         Sharing input
  188.         -------------
  189.  
  190.         The  above  mentioned  restrictions  imposed  by  the  concurrent 
  191.         programming model are most severe when reading from the keyboard. 
  192.         Whenever a <read (input,  ....)> is coded, Turbo Pascal transfers 
  193.         control  to  a library procedure that accepts keyboard input  and 
  194.         only returns when "Enter" is pressed. While this is happening, no 
  195.         calls to <switchtask> are made : the entire program "hangs" until 
  196.         input is ready. 
  197.  
  198.         CONCUR.PAS  contains a replacement "CON" input driver that avoids 
  199.         this behavior :  <conin>. <Conin> checks the keyboard for any key 
  200.         pressed.  If none is pressed, it saves the library variables that 
  201.         are  shared  with other library routines on the stack  and  calls 
  202.         <switchtask> until a key is pressed.  Other tasks may continue to 
  203.         run. 
  204.  
  205.         The  keyboard must be shared when two or more tasks  need  input. 
  206.         The  procedures  <claiminput> and <releaseinput> takes  care  for 
  207.         that.   A  task  needing  input  calls  <claiminput>.   The  flag 
  208.         <inputbusy>  is set.  The task now gets control over <input>.  As 
  209.         soon as input is completed,  <releaseinput> is called,  releasing 
  210.         the keyboard for other tasks.
  211.  
  212.         Another task needing input should call <claiminput> too. It finds 
  213.         <inputbusy>  set.  <Switchtask>  is called until the  first  task 
  214.         claiming <input> releases the keyboard,  allowing the second task 
  215.         to take control over the keyboard.
  216.  
  217.  
  218.         Sharing output
  219.         --------------
  220.  
  221.         The above mentioned sharing problems also exist for output to the 
  222.         screen.  CONCUR.PAS therefor contains a replacement screen output 
  223.         driver  <conout>.  Each concurrent task has its own window on the 
  224.         screen.  Initially,  each task gets a window of full screen size. 
  225.         Upon activation,  the task should call the procedure <window>  to 
  226.         shrink  its  window to a part of the screen,  reserved  for  that 
  227.         task.  The  procedure  <border> optionally draws a border  around 
  228.         that window. 
  229.  
  230.         CONCUR.PAS  contains  a  replacement for  all  screen  associated 
  231.         standard Turbo Pascal procedures.  These procedures are now  just 
  232.         effective  for  the window of the task that calls the  procedure. 
  233.         Example  :  <clrscr> now just clears the window of the  task  and 
  234.         leaves  the  rest of the screen untouched.  A list of  procedures 
  235.         effected follows :
  236.  
  237.         - Window : sets up a window for the current task.
  238.         - InsLine/DelLine. Effective in the task window only.
  239.         - ClrEol/ClrScr. Effective in the task window only.
  240.         - GotoXY. Uses the screen coordinates in the task window.
  241.         - WhereX/WhereY. Returns coordinates from the task window.
  242.  
  243.  
  244.                                         4
  245.  
  246.  
  247.  
  248.         - TextColor/TextBackground.  Changes  colors in the  task  window 
  249.              only.
  250.         - LowVideo/NormVideo. As above.
  251.  
  252.         Procedure  <clearscreen>  has been redefined to clear the  entire 
  253.         screen, regardless of the task calling the procedure.
  254.  
  255.  
  256.  
  257.         Procedure entries
  258.         -----------------
  259.  
  260.         The following procedures in CONCUR.PAS may be called :
  261.  
  262.         - TaskInit  (stack_request :  integer).  This procedure  MUST  be 
  263.              called   at  program  start-up.   It  initializes  all  data 
  264.              structures  and module parts.  The procedure must be  called 
  265.              with  one parameter :  the sum of all stack sizes for  every 
  266.              task, expressed in paragraphs (16 bytes).
  267.  
  268.         - InstallTask (address,  stack_size, task_number : integer). This 
  269.              procedure should be called to install a task.  <Address>  is 
  270.              the  start  address  of the task and  is  obtained  by  "ofs 
  271.              (task)".  <Stack_size>  is the amount of stack needed by the 
  272.              task (in paragraphs).  <Task_number> is the task number  for 
  273.              the installed task and should range from 1 to 15.
  274.  
  275.         - SwitchTask.  This  procedure  does the context switch from  the 
  276.              currently active task to the next installed task. 
  277.  
  278.         - Window (x1,y1,x2,y2 : integer). This procedure should be called 
  279.              by  each  task (including task 0,  the main program  !!)  to 
  280.              reserve a portion of the screen for its output. <x1, y1> are 
  281.              the screen coordinates for the upper left corner,  <x2,  y2> 
  282.              is the lower right corner. 
  283.  
  284.         - Border (name).  Draws a border around the window of the current 
  285.              task. <name> is shown on top of the window.
  286.  
  287.         - ClearScreen. May be called to clear the entire screen.
  288.  
  289.  
  290.         Program design
  291.         --------------
  292.  
  293.         The following basic steps should be made when designing a program 
  294.         using the concurrent programming method :
  295.  
  296.         1. Make  a  logical  division in your program into  actions  that 
  297.              should  occur  concurrently.  This will produce  the  tasks. 
  298.              Remember  that  the main program always acts as one  of  the 
  299.              tasks,  you  may want to use it to monitor the execution  of 
  300.              the entire program.  Avoid having too many tasks,  the  more 
  301.              tasks  are present,  the slower each of them will run.  
  302.  
  303.  
  304.  
  305.                                         5
  306.  
  307.  
  308.  
  309.  
  310.         2. Choose  a screen layout for the windows of each task producing 
  311.              screen output.  If you have a color monitor, be liberal with 
  312.              the use of TextColor and TextBackground. Have the tasks call 
  313.              <window>/<border>  as  soon  as they  start  execution.   
  314.  
  315.         3. Write the code for each concurrent task. Decide where calls to 
  316.              <switchtask> should be made.  Each concurrent task should at 
  317.              least  contain an infinite loop,  so a call should  be  made 
  318.              inside that loop. Inspect other loops and place calls at the 
  319.              deepest nesting levels. 
  320.  
  321.         4. Include the include-files :
  322.                   1. {$i biosdec.pas}   {BIOS-call declarations}
  323.                   2. {$i libdec.pas}    {Turbo library declarations}
  324.                   3. {$i concur.pas}    {Concurrent programming kernel}
  325.  
  326.         5. Inspect your code for tasks that need input from the keyboard. 
  327.              If  at least two tasks need input,  surround the input  code 
  328.              with calls to <claiminput> and <releaseinput>.
  329.  
  330.         6. Inspect your code for shared access to global variables, files 
  331.              and  devices.  Access  may need to be controlled through  an 
  332.              access flag. Example :
  333.  
  334.              procedure claimvar;                procedure task1;
  335.              begin                              .....
  336.                while var_in_use do switchtask;  claimvar;
  337.                var_in_use := true;              ....
  338.              end;                               releasevar;
  339.  
  340.              procedure releasevar;              procedure task2;
  341.              begin                              ....
  342.                var_in_use := false;             claimvar;
  343.              end;                               ....
  344.                                                 releasevar;
  345.  
  346.  
  347.         A prototype task would be coded as follows :
  348.          
  349.         procedure task1;
  350.         begin
  351.           textbackground (..);    {Background color for task window}
  352.           window (....);          {Create task window}
  353.           textcolor (..);         {Color for border}
  354.           border (....);          {Create border around window}
  355.           textcolor (..);         {Color for text in window}
  356.           repeat
  357.             ....
  358.             switchtask;           {At least one call to switchtask}
  359.           until false;            {Task should contain infinite loop}
  360.         end;
  361.  
  362.  
  363.  
  364.  
  365.  
  366.                                         6
  367.  
  368.  
  369.  
  370.         Installing CONCUR
  371.         -----------------
  372.  
  373.         CONCUR  is  highly compiler version specific,  due to the use  of 
  374.         several  addresses  in  the code and data segments of  the  Turbo 
  375.         Pascal library. You will find the declarations of these locations 
  376.         in  the  include  file "LIBDEC.PAS".  The  declared  values  were 
  377.         obtained  with  version  3.01A,  emulating real  math.  To  check 
  378.         whether your compiler can work with these values, compile and run 
  379.         the program "VERSION.PAS".  It will tell you want changes must be 
  380.         made for your compiler.  If the message "Cannot find address  for 
  381.  
  382.  
  383.  
  384.         Restrictions
  385.         ------------
  386.  
  387.         1. Do  not  use  the  $P and $G  compiler  switches.  They  will 
  388.              circumvent  calls  to the replacement <conin>  and  <conout> 
  389.              drivers.
  390.         2. Control-S/Q  will no longer work during screen output,  due to 
  391.              the replacement <conout> driver.
  392.         3. Access to global variables by more than one task is dangerous. 
  393.              A  sharing protocol is needed,  similar to  the  <inputbusy> 
  394.              method employed by the input driver.
  395.         4. A maximum of 15 tasks may be installed. 
  396.         5. If you have a 8088 CPU based system (the IBM PC and XT models) 
  397.              some  changes have to be made in the <switchtask> procedure. 
  398.              See code for details.
  399.         6. The installed tasks MUST be parameterless !!!
  400.         7. Be very  careful when using overlays in your code.  An overlay 
  401.              procedure that is overlayed with a procedure used by another 
  402.              task should never call <switchtask>. 
  403.  
  404.  
  405.         Problems and error messages
  406.         ---------------------------
  407.  
  408.         The  following  error messages may appear,  they will  abort  the 
  409.         program :
  410.  
  411.         - "Stack  overflow in task ..".  You did not reserve enough stack 
  412.              space when calling <installtask>. Increase the number.
  413.         - "Unexpected  abortion  of task ..".  Installed  tasks  are  not 
  414.              allowed  to  abort.  Revise your code and make sure  a  task 
  415.              contains an infinite loop.
  416.         - "Illegal task number ..". Task numbers should be in (1..15).
  417.         - "Not enough stack to install task ..".  A call to <installtask> 
  418.              requested more stack space than was allocated by <taskinit>.
  419.         - "Double  installation  of  task ..".  Task  numbers  should  be 
  420.              unique.
  421.  
  422.         Failing  to  request enough stack space for a task may result  in 
  423.         erratic behavior of the program. The stack of another task may be 
  424.         destroyed without Turbo or CONCUR detecting the error.  Make sure 
  425.  
  426.  
  427.                                         7
  428.  
  429.  
  430.  
  431.         enough stack space is reserved for each task. As a rule of thumb, 
  432.         add  the size of all local variables and divide by 16 to get  the 
  433.         number  of paragraphs.  Add 20 for safety.  If the task does  any 
  434.         recursion,  add  plenty more (depending on the maximum  recursion 
  435.         depth).
  436.  
  437.         A  stack  overflow occasionally may be detected by the  run  time 
  438.         code  and  will  result  in Turbo run  time  error  message  <FF> 
  439.         (heap/stack collision) or the message "Stack overflow in task".
  440.  
  441.  
  442.         Files in CONCUR.ARC
  443.         -------------------
  444.  
  445.         1. CONCUR.PAS   : the concurrent programming module. 
  446.         2. BIOSDEC.PAS  :  bios call declarations.  Should be included by 
  447.              any program before including CONCUR.PAS. 
  448.         3. LIBDEC.PAS   : declarations for variables in the library.
  449.         4. CPDEMO.PAS   : a simple demonstration program using CONCUR. To 
  450.              abort the program, press any key and type "halt <enter>".
  451.         5. CONCUR.DOC   : this file.
  452.         6. VERSION.PAS   :  compile  and run this program with  your  own 
  453.              Turbo Pascal compiler.  It will tell you if any changes have 
  454.              to be made in LIBDEC.PAS.
  455.  
  456.         Future
  457.         ------
  458.  
  459.         I am currently working on a true multi-tasking version of CONCUR. 
  460.         Expect versions of MULTI.ARC Real Soon Now.
  461.  
  462.         I am looking forward to your experience with  CONCUR.PAS.  Please 
  463.         let me now if you find any serious errors,  particularly the ones 
  464.         that  will  hang up your computer.  My user ID on the  COMPUSERVE 
  465.         network is [76266,2746].      
  466.  
  467.  
  468.                                                 - Hans Passant -
  469.  
  470.  
  471.  
  472.  
  473.  
  474.  
  475.  
  476.  
  477.  
  478.  
  479.  
  480.  
  481.  
  482.  
  483.  
  484.  
  485.  
  486.  
  487.  
  488.                                         8
  489.