home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / Misc / OB3.2D2.DMS / in.adf / Module / Concurrency.mod < prev    next >
Encoding:
Text File  |  1994-08-05  |  10.9 KB  |  430 lines

  1. (*-------------------------------------------------------------------------*)
  2. (*                                                                         *)
  3. (*  Amiga Oberon Interface Module: Concurrency        Date: 02-Nov-92      *)
  4. (*                                                                         *)
  5. (*   © 1992 by Fridtjof Siebert                                            *)
  6. (*                                                                         *)
  7. (*-------------------------------------------------------------------------*)
  8.  
  9. MODULE Concurrency;
  10.  
  11. IMPORT BT * := BasicTypes,
  12.                Exec,
  13.                OberonLib,
  14.                Alerts,
  15.                Strings,
  16.        GC   := GarbageCollector,
  17.                SYSTEM,
  18.                Dos *;
  19.  
  20. TYPE
  21.   Waiting   = POINTER TO WaitingDesc;
  22.   Process * = POINTER TO ProcessDesc;
  23.  
  24.   ProcessProc * = PROCEDURE (data: BT.ANY): BT.ANY;
  25.  
  26.   ProcessDesc * = RECORD (BT.ANYDesc)
  27.     next: Process;                (* internal link
  28.                                    *)
  29.     dosProcess-: Dos.ProcessPtr;  (* AmigaOS-Process structure of this
  30.                                    * process
  31.                                    *)
  32.     done: BOOLEAN;                (* done=TRUE indicates that the process
  33.                                    * has terminated and result is valid.
  34.                                    *)
  35.     data: BT.ANY;         (* data passed to this process
  36.                                    *)
  37.     result: BT.ANY;       (* Result of this process. Only valid if
  38.                                    * done=TRUE.
  39.                                    *)
  40.     taskTrapData: OberonLib.TaskTrapData;
  41.                                   (* New Process' Task.trapData will point
  42.                                    * to this structure.
  43.                                    *)
  44.     mutator: GC.Mutator;          (* GC's mutator for this process
  45.                                    *)
  46.     proc: ProcessProc;            (* The main process procedure
  47.                                    *)
  48.     priority: SHORTINT;           (* New Process' priority
  49.                                    *)
  50.     waiting: Waiting;             (* Processes waiting for our termination
  51.                                    *)
  52.   END;
  53.  
  54.   WaitingDesc = RECORD (BT.ANYDesc)
  55.     next: Waiting;                (* link waiting processes
  56.                                    *)
  57.     task: Exec.TaskPtr;           (* waiting task
  58.                                    *)
  59.   END;
  60.  
  61. VAR
  62.   Processes: Process;
  63.  
  64.   FakeSegList: STRUCT  (* needed for OS1.3's CreateProc()
  65.                         * (the compiler longword aligns this automatically)
  66.                         *)
  67.     length: LONGINT;
  68.     next: Exec.BPTR;
  69.  
  70.  
  71.     jmp: INTEGER;      (* JMP  StartProcess *)
  72.     start: PROCEDURE;
  73.   END;
  74.  
  75.   ProcessesSemaphore: Exec.SignalSemaphore;
  76.                        (* Semaphore to access global variables of this
  77.                         * module
  78.                         *)
  79.  
  80.   ProcessName: POINTER TO ARRAY 80 OF CHAR;
  81.  
  82.   WakeUpSig: INTEGER;
  83.  
  84.   DefaultStackSize: LONGINT;
  85.  
  86.   ProcessInTrapData: INTEGER;     (*
  87.                                    * Offset of Process in
  88.                                    * TaskTrapData.user[]
  89.                                    *)
  90.  
  91. PROCEDURE ChildHaltProc;
  92.  
  93. VAR
  94.   p,q: Process;
  95.  
  96. CONST
  97.   RTS = 4E75H;
  98.  
  99. BEGIN
  100.   p := SYSTEM.VAL(Process,OberonLib.execBase.thisTask.trapData.user[ProcessInTrapData]);
  101.   Exec.ObtainSemaphore(ProcessesSemaphore);
  102.   Exec.Forbid;
  103.   p.done := TRUE;
  104.   WHILE p.waiting#NIL DO
  105.     Exec.Signal(p.waiting.task,LONGSET{WakeUpSig});
  106.     p.waiting := p.waiting.next;
  107.   END;
  108.   IF p=Processes THEN
  109.     Processes := p.next
  110.   ELSE
  111.     q := Processes;
  112.     WHILE q.next#p DO q := q.next END;
  113.     q.next := p.next;
  114.   END;
  115.   Exec.ReleaseSemaphore(ProcessesSemaphore);
  116. (* $IF GarbageCollector *)
  117.   GC.RemMutator(p.mutator);
  118. (* $END *)
  119.   SYSTEM.SETREG(15,OberonLib.execBase.thisTask.trapData.oldSP);
  120.   SYSTEM.INLINE(RTS);
  121. END ChildHaltProc;
  122.  
  123.  
  124. PROCEDURE Call(p: Process);
  125. (*
  126.  * Sets p's priority, calls p.proc and terminates this process.
  127.  *)
  128. BEGIN
  129.   IF Exec.SetTaskPri(Exec.exec.thisTask,p.priority)=0 THEN END;
  130.   p.result := p.proc(p.data);
  131.   ChildHaltProc;
  132. END Call;
  133.  
  134.  
  135. VAR
  136.   StartProcessProc: Process;
  137.  
  138.  
  139. PROCEDURE StartProc;
  140. (*
  141.  * adds new mutator to GC and calls Call() to execute this process' procedure.
  142.  *
  143.  * $StackChk-
  144.  *)
  145.  
  146. BEGIN
  147.   StartProcessProc := SYSTEM.VAL(Process,OberonLib.execBase.thisTask.trapData.user[ProcessInTrapData]);
  148.  
  149. (* $IF GarbageCollector *)
  150.  
  151.     IF GC.AddMutator(StartProcessProc.mutator) THEN
  152.  
  153. (* $END *)
  154.  
  155.       Call(StartProcessProc);
  156.  
  157. (* $IF GarbageCollector *)
  158.  
  159.     ELSE
  160.  
  161.       StartProcessProc.done := TRUE;
  162.       Processes := StartProcessProc.next;
  163.                            (* because NewProcessX does a ReleaseSemaphore(ProcessesSemaphore)
  164.                             * after it does Permit(), and this processes priority is 127,
  165.                             * we are safe to modify the Processes-List.
  166.                             *)
  167.  
  168.     END;
  169.  
  170. (* $END *)
  171.  
  172. END StartProc;
  173.  
  174.  
  175. PROCEDURE StartProcess;
  176. (*
  177.  * Sets A5 and trapData.oldSP
  178.  *
  179.  * $StackChk-
  180.  *)
  181.  
  182. BEGIN
  183.  
  184.   OberonLib.SetA5;
  185.  
  186.   (* $IF SmallData *)
  187.     OberonLib.execBase.thisTask.trapData.oldSP := SYSTEM.REG(15);
  188.   (* $ELSE *)
  189.     OberonLib.execBase.thisTask.trapData.oldSP := SYSTEM.VAL(LONGINT,SYSTEM.REG(15))-4;
  190.   (* $END *)
  191.  
  192.   StartProc;
  193.  
  194. END StartProcess;
  195.  
  196.  
  197. (* ------  NewProcessX:  ------ *)
  198.  
  199.  
  200. PROCEDURE NewProcessX * (proc: ProcessProc;
  201.                          data: BT.ANY;
  202.                          stackSize: LONGINT;
  203.                          priority: SHORTINT): Process;
  204. (*
  205.  * Start proc as new concurrent process. data will be passed to proc as
  206.  * procedure paramter.
  207.  *
  208.  * proc must be reentrant, ie. it must not access global variables or data
  209.  * structures or call procedures that access global data while other
  210.  * processes may access this data.
  211.  *
  212.  * To be save do not use any global variables within this routine.
  213.  *
  214.  * The new process will have a Stack that is stackSize bytes large. The new
  215.  * process' task priority will be set to priority.
  216.  *
  217.  * This procedure is reentrant and may be called from any process to
  218.  * create new child-processes.
  219.  *
  220.  *)
  221.  
  222. VAR
  223.   newproc: Process;
  224.   msg: Dos.ProcessId;
  225.  
  226. BEGIN
  227.  
  228.   NEW(newproc);
  229.  
  230.   newproc.taskTrapData := OberonLib.execBase.thisTask.trapData^;
  231.   newproc.taskTrapData.mutator  := SYSTEM.ADR(newproc.mutator);
  232.   newproc.taskTrapData.haltProc := ChildHaltProc;
  233.   newproc.taskTrapData.user[ProcessInTrapData] := SYSTEM.VAL(SYSTEM.ADDRESS,newproc);
  234.   newproc.data         := data;
  235.   newproc.proc         := proc;
  236.   IF priority=127 THEN DEC(priority) END;
  237.   newproc.priority     := priority;
  238.   newproc.done         := FALSE;
  239.  
  240.   Exec.ObtainSemaphore(ProcessesSemaphore);
  241.  
  242.     Exec.Forbid;
  243.  
  244.       IF Dos.dos.lib.version<37 THEN
  245.  
  246.         FakeSegList.length := 16;
  247.         FakeSegList.next   := NIL;
  248.         FakeSegList.jmp    := 4EF9U;        (* JMP abslong *)
  249.         FakeSegList.start  := StartProcess;
  250.  
  251.         msg :=  Dos.CreateProc(ProcessName^,
  252.                                127,
  253.                                SYSTEM.ADR(FakeSegList.next),
  254.                                stackSize);
  255.         IF msg=NIL THEN
  256.           newproc.dosProcess := NIL;
  257.         ELSE
  258.           newproc.dosProcess := SYSTEM.VAL(Dos.ProcessPtr,SYSTEM.VAL(LONGINT,msg)-SIZE(Exec.Task));
  259.         END;
  260.  
  261.       ELSE
  262.  
  263.         newproc.dosProcess := Dos.CreateNewProcTags(
  264.                                     Dos.npEntry    ,SYSTEM.VAL(SYSTEM.ADDRESS,StartProcess),
  265.                                     Dos.npStackSize,stackSize,
  266.                                     Dos.npName     ,ProcessName,
  267.                                     Dos.npPriority ,127);
  268.  
  269.       END;
  270.  
  271.       IF newproc.dosProcess#NIL THEN
  272.         newproc.dosProcess.task.trapData := SYSTEM.ADR(newproc.taskTrapData);
  273.         newproc.dosProcess.task.trapCode := Exec.exec.thisTask.trapCode;
  274.         newproc.next := Processes;
  275.         Processes    := newproc;
  276.       ELSE
  277.         newproc := NIL;
  278.       END;
  279.   
  280.     Exec.Permit;
  281.  
  282. (*
  283.  * Because the new process' priority is 127, it will run now. It will add its
  284.  * mutator to the GC and maintain a copy of a pointer to its Process-structure
  285.  * in a local variable within Call(). Then it will set its priority to the
  286.  * specified priority. So we are safe to return now, even if our caller
  287.  * deletes the result, which else would be the only reference to the Process.
  288.  *)
  289.  
  290.   Exec.ReleaseSemaphore(ProcessesSemaphore);
  291.  
  292.   RETURN newproc;
  293.  
  294. END NewProcessX;
  295.  
  296.  
  297. (* ------  NewProcess:  ------ *)
  298.  
  299.  
  300. PROCEDURE NewProcess * (proc: ProcessProc;
  301.                         data: BT.ANY): Process;
  302. (*
  303.  * Start proc as new concurrent process. data will be passed to proc as
  304.  * procedure paramter.
  305.  *
  306.  * proc must be reentrant, ie. it must not access global variables or data
  307.  * structures or call procedures that access global data while other
  308.  * processes may access this data.
  309.  *
  310.  * To be save do not use any global variables within this routine.
  311.  *
  312.  * The stackSize and priority will be inherited from this process.
  313.  *
  314.  * This procedure is reentrant and may be called from any process to
  315.  * create new child-processes.
  316.  *)
  317.  
  318. BEGIN
  319.   RETURN NewProcessX(proc,
  320.                      data,
  321.                      DefaultStackSize,
  322.                      Exec.exec.thisTask.node.pri);
  323. END NewProcess;
  324.  
  325.  
  326. (* ------  Wait:  ------ *)
  327.  
  328.  
  329. PROCEDURE (p: Process) Wait * (): BT.ANY;
  330.  
  331. (*
  332.  * Wait for process p to terminate. Returns its result.
  333.  *)
  334.  
  335. VAR
  336.   wait: Waiting;
  337.  
  338. BEGIN
  339.   Exec.ObtainSemaphore(ProcessesSemaphore);
  340.   IF ~ p.done THEN
  341.     NEW(wait);
  342.     wait.task := Exec.exec.thisTask;
  343.     wait.next := p.waiting;
  344.     p.waiting := wait;
  345.     Exec.ReleaseSemaphore(ProcessesSemaphore);
  346.     REPEAT
  347.     UNTIL p.done OR (WakeUpSig IN Exec.Wait(LONGSET{WakeUpSig}));
  348.   ELSE
  349.     Exec.ReleaseSemaphore(ProcessesSemaphore);
  350.   END;
  351.   RETURN p.result;
  352. END Wait;
  353.  
  354.  
  355. (* ------  isRunning:  ------ *)
  356.  
  357.  
  358. PROCEDURE (p: Process) isRunning * (): BOOLEAN;
  359.  
  360. (*
  361.  * Check if p is still running.
  362.  *)
  363.  
  364. BEGIN
  365.   RETURN ~ p.done
  366. END isRunning;
  367.  
  368.  
  369. (* ------  WaitForAllProcesses:  ------ *)
  370.  
  371.  
  372. PROCEDURE WaitForAllProcesses * ;
  373. (*
  374.  * Wait for all processes to terminate
  375.  *)
  376. VAR
  377.   p: Process;
  378. BEGIN
  379.   LOOP
  380.     Exec.ObtainSemaphore(ProcessesSemaphore);
  381.     p := Processes;
  382.     Exec.ReleaseSemaphore(ProcessesSemaphore);
  383.     IF p=NIL THEN EXIT END;
  384.     IF p.Wait()=NIL THEN END;
  385.   END;
  386. END WaitForAllProcesses;
  387.  
  388.  
  389. (* ------  Init:  ------ *)
  390.  
  391.  
  392. BEGIN
  393.  
  394.   Exec.InitSemaphore(ProcessesSemaphore);
  395.  
  396.   Processes := NIL;
  397.  
  398.   NEW(ProcessName);
  399.   IF Exec.exec.thisTask.node.name#NIL THEN
  400.     COPY(Exec.exec.thisTask.node.name^,ProcessName^);
  401.   ELSE
  402.     ProcessName^ := "Amiga Oberon";
  403.   END;
  404.  
  405.   ProcessName[55] := 0X; (* Shrink ProcessName if it's too long *)
  406.   Strings.Append(ProcessName^," background process");
  407.  
  408.   DefaultStackSize := OberonLib.OldSP.stackSize;
  409.  
  410.   WakeUpSig := Exec.AllocSignal(-1);
  411.   ProcessInTrapData := OberonLib.AllocUser();
  412.   IF (WakeUpSig<0) OR (ProcessInTrapData<0) THEN
  413.     HALT(20)
  414.   END;
  415.  
  416. CLOSE
  417.  
  418.   WaitForAllProcesses;
  419.  
  420.   IF WakeUpSig>=0 THEN Exec.FreeSignal(WakeUpSig) END;
  421.   IF ProcessInTrapData>=0 THEN OberonLib.FreeUser(ProcessInTrapData) END;
  422.  
  423. END Concurrency.
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.