home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / multtsk / cpm25d / pro_con.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1994-04-28  |  14.4 KB  |  423 lines

  1. {$I cpmswitc.inc}
  2. {$M 16384,0,655360 }
  3.  
  4. {--------------------------------------------------------------------------
  5.  
  6. PRO_CON.PAS  (Demo: The producer-consumer problem)
  7.  
  8. This program requires the CPMULTI Multitasking Toolkit and Turbo Pascal
  9. 5.0 or later.
  10.  
  11. January 1994
  12.  
  13. Copyright (C) 1994 (USA)        Copyright (C) 1989-1994
  14. Hypermetrics                    Christian Philipps Software-Technik
  15. PO Box 9700 Suite 363           Duesseldorfer Str. 316
  16. Austin, TX  78758-9700          D-47447 Moers
  17.                                 Germany
  18.  
  19. This is a presentation of the producer-consumer problem as an example of 
  20. keyboard I/O control.
  21.  
  22. Functional description:
  23.   Input characters from the keyboard and output to the screen.
  24.   Esc terminates the program.
  25.   ^S  suspends the output until ^Q is received; in the meanwhile,
  26.       incoming characters are stored in a circular queue and not
  27.       displayed yet.
  28.   ^Q  reactivates screen output; the stored buffer contents are 
  29.       immediately displayed.
  30.  
  31. ---------------------------------------------------------------------------}
  32.  
  33. program ProducerConsumer;
  34.  
  35. uses CRT, CPMulti;
  36.  
  37. const RBuffSize = 36;                      { Size of the circular queue. }
  38.       CritLin   = 15;
  39.       CritCol   = 51;
  40.       FullLin   = 15;
  41.       FullCol   = 34;
  42.       EmptyLin  = 15;
  43.       EmptyCol  = 42;
  44.       PEndLin   = 15;
  45.       PEndCol   = 12;
  46.       OutLin    = 15;
  47.       OutCol    = 24;
  48.  
  49. var   RBuff     : record
  50.                     Buff     : array[0..RBuffSize] of Char;
  51.                                                  { Queue; 1 element reserved
  52.                                                    for easier manipulation. }
  53.                     Critical : Pointer;          { Semaphore for synchronizing
  54.                                                   access. }
  55.                     Full     : Pointer;          { Semaphore: Used buffer 
  56.                                                    slots. }
  57.                     Empty    : Pointer;          { Semaphore: Free buffer
  58.                                                    slots. }
  59.                     Head     : Byte;             { Head and tail pointers. }
  60.                     Tail     : Byte;
  61.                   end;
  62.  
  63.       OutputSem : Pointer;                       { Semaphore for output
  64.                                                    control. }
  65.       ProgramEnd: Pointer;                       { Signal program end. }
  66.       ConsumerNo,                                { Task ID's. }
  67.       ProducerNo: TaskNoType;
  68.  
  69. {-----------------------------------------------------------------------------}
  70.  
  71. function NextSlot(S:Byte):Byte;
  72.  
  73. { Determine next buffer slot following S. }
  74.  
  75. begin
  76.   NextSlot := Succ(S) mod RBuffSize;
  77. end;
  78.  
  79. {-----------------------------------------------------------------------------}
  80.  
  81. procedure WriteCharXY(X,Y:Byte; C:Char);
  82.  
  83. { Output a character at position X,Y. By means of appropriate
  84.   CPU-blocking it is ensured that the sequence GotoXY-and-Write
  85.   is never interrupted by a task switch. }
  86.  
  87. begin
  88.   BindCPU;
  89.   GotoXY(X,Y);
  90.   Write(C);
  91.   ReleaseCPU;
  92. end;
  93.  
  94. {-----------------------------------------------------------------------------}
  95.  
  96. procedure WriteByteXY(X,Y,B:Byte);
  97.  
  98. { Output a byte value at position X,Y. See also WriteCharXY. }
  99.  
  100. begin
  101.   BindCPU;
  102.   GotoXY(X,Y);
  103.   Write(B:2);
  104.   ReleaseCPU;
  105. end;  {WriteByteXY}
  106.  
  107. {-----------------------------------------------------------------------------}
  108.  
  109. procedure Status;
  110.  
  111. { Display the task status. }
  112.  
  113. begin
  114.   BindCPU;
  115.   GotoXY(65,9);
  116.   Write(StateText[GetTaskState(ConsumerNo)]:10);
  117.   GotoXY(65,22);
  118.   Write(StateText[GetTaskState(ProducerNo)]:10);
  119.   ReleaseCPU;
  120. end;
  121.  
  122. {-----------------------------------------------------------------------------}
  123.  
  124. procedure SW(S:Pointer; c,l:byte);
  125.  
  126. { Call SemWait with visual feedback. }
  127.  
  128. begin
  129.   WriteByteXY(C,L,SemGetSignals(S));
  130.   SemWait(S);
  131.   WriteByteXY(C,L,SemGetSignals(S));
  132.   Status;
  133. end;
  134.  
  135. {-----------------------------------------------------------------------------}
  136.  
  137. procedure SS(S:Pointer; c,l:byte);
  138.  
  139. { Call SemSignal with visual feedback. }
  140.  
  141. begin
  142.   SemSignal(S);
  143.   WriteByteXY(C,L,SemGetSignals(S));
  144.   Status;
  145. end;
  146.  
  147. {-----------------------------------------------------------------------------}
  148.  
  149. function RBuffPut(C:Char):Boolean;
  150.  
  151. {---------------------------------------------------------------------------
  152. Store a character in the buffer. Return True if successful, False if a 
  153. buffer overflow occurred. Since the output control influences the behavior of
  154. the output task (^S and ^Q), the input control will never be blocked for a 
  155. longer time with the storing of characters in the buffer. But this would be
  156. the case, if it always waited for the freeing of a buffer slot. If the output
  157. control after receipt of a ^S is not in the position to empty a slot and the
  158. input control is blocked by a Wait for a storage slot, the entire system will
  159. hang irrevocably!
  160.  
  161. WARNING! Note that the positioning of the Wait call on the synchronization
  162. semaphore is critical! Since a blocking through a Wait for Empty is dependent
  163. on the signal count of the Empty semaphore, THEORETICALLY in the time between
  164. accessing the signal count and the Wait on the Empty semaphore, another task
  165. could have grabbed the last slot in the buffer. You must ensure by proper
  166. delimiting of the critical section that until the final occupying of the
  167. buffer slot, no other task can gain access to the buffer.
  168. ----------------------------------------------------------------------------}
  169.  
  170. begin
  171.   with RBuff do
  172.   begin
  173.     SW(Critical,CritCol,CritLin);                { Exclusive buffer access. }
  174.     if SemGetSignals(Empty) = 0 then             { Buffer full. }
  175.       RBuffPut := False                          { Prevent blocking. }
  176.     else
  177.     begin
  178.       RBuffPut := True;
  179.       SW(Empty,EmptyCol,EmptyLin);       { Reserve a slot. }
  180.       Buff[Tail] := c;                   { Store characters. }
  181.       WriteCharXY(21+Tail,19,' ');
  182.       if C = #13 then 
  183.         WriteCharXY(21+Tail,21,#188)
  184.       else 
  185.         WriteCharXY(21+Tail,21,c);
  186.       Tail := NextSlot(Tail);            { Head pointer to next slot. }
  187.       WriteCharXY(21+Tail,19,#25);
  188.       SS(Full,FullCol,FullLin);          { One more character in the
  189.                                            buffer. }
  190.     end;
  191.     SS(Critical,CritCol,CritLin);                { Release the buffer. }
  192.   end;
  193. end;
  194.  
  195. {-----------------------------------------------------------------------------}
  196.  
  197. function RBuffGet:Char;
  198.  
  199. { Take the first character from the buffer and make it available to the 
  200.   application. If the buffer is empty, wait for the arrival of a character. }
  201.  
  202. begin
  203.   with RBuff do
  204.   begin
  205.     SW(Full,FullCol,FullLin);                    { Request characters. }
  206.     SW(Critical,CritCol,CritLin);                { Exclusive access. }
  207.     RBuffGet := Buff[Head];                      { Grab characters. }
  208.     WriteCharXY(21+Head,23,' ');
  209.     Head := NextSlot(Head);                      { Update the head pointer. }
  210.     WriteCharXY(21+Head,23,#24);
  211.     SS(Critical,CritCol,CritLin);                { Release the buffer. }
  212.     SS(Empty,EmptyCol,EmptyLin);                 { One more slot free. }
  213.   end;
  214. end;
  215.  
  216. {-----------------------------------------------------------------------------}
  217.  
  218. {$F+}
  219. procedure Producer(P:Pointer);
  220.  
  221. { Input control: Accepts all available characters from the keyboard into the
  222. buffer. If a ^S is received, the output of characters is postponed until the
  223. receipt of a ^Q. }
  224.  
  225. var   C       : Char;
  226.       Display : Boolean;
  227.       Col     : Byte;
  228. begin
  229.   Display := True;                               { Display enabled. }
  230.   Col := 1;
  231.   repeat                                         { Infinite loop. }
  232.     while Keypressed do
  233.     begin
  234.       C := ReadKey;
  235.       case C of
  236.         ^S: if Display then 
  237.             begin
  238.               SW(OutputSem,OutCol,OutLin); { Output turned off. }
  239.               Display := False;            { Note the status. }
  240.             end;
  241.         ^Q: if not Display then            { Output turned off? }
  242.             begin
  243.               SS(OutputSem,OutCol,OutLin); { Output released. }
  244.               Display := True;             { Store new status. }
  245.             end;
  246.       else                                 { No special characters. }
  247.         if not RBuffPut(C) then         { Overflow. }
  248.         begin
  249.           BindCPU;                      { Atomic action. }
  250.           GotoXY(34,18);
  251.           TextBackground(White);
  252.           TextColor(Black);
  253.           Write(' Overflow ');
  254.           TextColor(White);
  255.           TextBackground(Black);
  256.           ReleaseCPU;                   { End atomic action. }
  257.         end;
  258.       end; {Case}
  259.     end;
  260.     Sched;                              { All characters processed; }
  261.   until False;                          {   yield the timeslice.    }
  262.  
  263. end;
  264.  
  265. {-----------------------------------------------------------------------------}
  266.  
  267. procedure Consumer(P:Pointer);
  268.  
  269. {---------------------------------------------------------------------------
  270. This is the task which takes waiting characters from the buffer and outputs
  271. them to the screen. If a ^S is received from the input controller, then
  272. this grabs the output semaphore and so takes care of blocking the output
  273. task the next time it tries to output a character onto the screen. With the
  274. arrival of a ^Q, this semaphore is released again. If the ESC character is
  275. taken from the buffer, this task effects the termination of the program
  276. by incrementing the signal count of the semaphore ProgramEnd. The consumer
  277. task runs with highest priority (since it spends most of its time waiting for
  278. input anyway) and therefore does not hinder other tasks. If there are
  279. characters waiting, these are processed immediately in the interest of a
  280. fast reaction time.
  281. ---------------------------------------------------------------------------- }
  282.  
  283. const  MaxCols = 50;
  284. var  C   : Char;
  285.      Col : Byte;
  286. begin
  287.   Col := 1;
  288.   repeat                                         { Infinite loop. }
  289.     C := RBuffGet;                               { Get characters. }
  290.     GotoXY(34,18);                               { Erase overflow text. }
  291.     Write('          ');
  292.     if C = #27 then 
  293.       SS(ProgramEnd,PendCol,PendLin)       { End the program. }
  294.     else 
  295.     begin
  296.       SW(OutPutSem,OutCol,OutLin);         { Wait for permission to output. }
  297.       if (Col >= MaxCols) or (C=#13) then  { Display overflow or CR. }
  298.       begin
  299.         BindCPU;                           { Critical section. }
  300.         GotoXY(7,8);
  301.         for Col := 1 to MaxCols do
  302.           Write(' ');
  303.         ReleaseCPU;                        { End critical section. }
  304.         Col := 1;
  305.       end;
  306.       if C <> #13 then                     { Character output. }
  307.       begin
  308.         WriteCharXY(6+Col,8,C);
  309.         Inc(Col);
  310.       end;
  311.       SS(OutPutSem,OutCol,OutLin);         { Set sempahore again. }
  312.     end;
  313.   until False;
  314. end;
  315. {$F-}
  316.  
  317. {-----------------------------------------------------------------------------}
  318.  
  319. procedure DrawScreen;
  320. begin
  321.   ClrScr;
  322.   BindCPU;
  323.   GotoXY(15,1);
  324.   Write('P R O C E S S     S Y N C H R O N I Z A T I O N');
  325.   GotoXY(18,3);
  326.   Write('Illustration of the producer-consumer problem.');
  327.   GotoXY(24,4);
  328.   Write('Author: Christian Philipps, 6/88');
  329.   GotoXY(5,7);
  330.   Write('┌───────────────────────────────────────────────────┐');
  331.   GotoXY(5,8);
  332.   Write('│                                                   │ Consumer Task');
  333.   GotoXY(5,9);
  334.   Write('└───────────────────────────────────────────────────┘');
  335.   GotoXY(6,12);
  336.   Write('┌────────────┬───────────┬──────┬───────┬──────────┐');
  337.   GotoXY(6,13);
  338.   Write('│ ProgramEnd │ OutputSem │ Full │ Empty │ Critical │ Semaphores for');
  339.   GotoXY(6,14);
  340.   Write('├────────────┼───────────┼──────┼───────┼──────────┤ task and access');
  341.   GotoXY(6,15);
  342.   Write('│            │           │      │       │          │ synchronization');
  343.   GotoXY(6,16);
  344.   Write('└────────────┴───────────┴──────┴───────┴──────────┘');
  345.   GotoXY(5,19);
  346.   Write('Head pointer');
  347.   GotoXY(20,20);
  348.   Write('┌────────────────────────────────────┐');
  349.   GotoXY(5,21);
  350.   Write('  Buffer   ->  │                                    │ Producer Task');
  351.   GotoXY(20,22);
  352.   Write('└────────────────────────────────────┘');
  353.   GotoXY(5,23);
  354.   Write('Tail pointer');
  355.   TextColor(Black);
  356.   TextBackground(White);
  357.   GotoXY(1,25);
  358.   Write(' Ctrl-S: Stop output   Ctrl-Q: Resume output   ESC: Exit program  ');
  359.   TextColor(White);
  360.   TextBackground(Black);
  361.   ReleaseCPU;
  362.   WriteCharXY(25,11,#30);
  363.   WriteCharXY(35,11,#30);
  364.   WriteCharXY(42,11,#30);
  365.   WriteCharXY(51,11,#30);
  366.   WriteCharXY(25,17,#30);
  367.   WriteCharXY(35,17,#30);
  368.   WriteCharXY(42,17,#30);
  369.   WriteCharXY(51,17,#30);
  370.   WriteCharXY(21,19,#25);
  371.   WriteCharXY(21,23,#24);
  372. end;
  373.  
  374. {-----------------------------------------------------------------------------}
  375.  
  376. function InitConPro:Boolean;
  377.  
  378. { Initialize the data structures and start the tasks. If an error occurs here,
  379.   the value False will be returned. }
  380.  
  381. begin
  382.   InitConPro := False;
  383.   with RBuff do
  384.   begin
  385.     FillChar(Buff,RBuffSize,' ');                { Blank the buffer. }
  386.     Head := 0;
  387.     Tail := 0;
  388.     if CreateSem(Critical) <> Sem_OK then        { Create the semaphores. }
  389.       Exit;
  390.     if CreateSem(Full) <> Sem_OK then 
  391.       Exit;
  392.     if CreateSem(Empty) <> Sem_OK then 
  393.       Exit;
  394.     SemSet(Empty,RBuffSize);                     { All slots are empty and }
  395.     SemClear(Full);                              { likewise, none are full. }
  396.   end;
  397.   if CreateSem(ProgramEnd) <> Sem_OK then        { Program-end flag. }
  398.     Exit;
  399.   SemClear(ProgramEnd);                          { Clear the flag.}
  400.   if CreateSem(OutputSem) <> Sem_OK then         { Semaphore for output }
  401.     Exit;                                        {   control.           }
  402.  
  403.   ConsumerNo := CreateTask(Consumer,nil,Pri_Kernel,500); { Create producer & }
  404.   ProducerNo := CreateTask(Producer,nil,Pri_User,500);   { consumer tasks.   }
  405.   if (ConsumerNo < 0) or (ProducerNo < 0) then
  406.     Exit;
  407.   DrawScreen;
  408.   InitConPro := True;
  409. end;
  410.  
  411. {-----------------------------------------------------------------------------}
  412.  
  413. begin
  414.   if not InitConPro then 
  415.   begin
  416.     Writeln('Error in initialization!');
  417.     Halt;
  418.   end;
  419.   { The main program stays "hung" in this case because of the SemWait
  420.     until a SemSignal on the semaphore ProgramEnd is issued. }
  421.   SW(ProgramEnd,PendCol,PendLin);
  422. end.
  423.