home *** CD-ROM | disk | FTP | other *** search
- (*-------------------------------------------------------------------------*)
- (* *)
- (* Amiga Oberon Interface Module: Concurrency Date: 02-Nov-92 *)
- (* *)
- (* © 1992 by Fridtjof Siebert *)
- (* *)
- (*-------------------------------------------------------------------------*)
-
- MODULE Concurrency;
-
- IMPORT BT * := BasicTypes,
- Exec,
- OberonLib,
- Alerts,
- Strings,
- GC := GarbageCollector,
- SYSTEM,
- Dos *;
-
- TYPE
- Waiting = POINTER TO WaitingDesc;
- Process * = POINTER TO ProcessDesc;
-
- ProcessProc * = PROCEDURE (data: BT.ANY): BT.ANY;
-
- ProcessDesc * = RECORD (BT.ANYDesc)
- next: Process; (* internal link
- *)
- dosProcess-: Dos.ProcessPtr; (* AmigaOS-Process structure of this
- * process
- *)
- done: BOOLEAN; (* done=TRUE indicates that the process
- * has terminated and result is valid.
- *)
- data: BT.ANY; (* data passed to this process
- *)
- result: BT.ANY; (* Result of this process. Only valid if
- * done=TRUE.
- *)
- taskTrapData: OberonLib.TaskTrapData;
- (* New Process' Task.trapData will point
- * to this structure.
- *)
- mutator: GC.Mutator; (* GC's mutator for this process
- *)
- proc: ProcessProc; (* The main process procedure
- *)
- priority: SHORTINT; (* New Process' priority
- *)
- waiting: Waiting; (* Processes waiting for our termination
- *)
- END;
-
- WaitingDesc = RECORD (BT.ANYDesc)
- next: Waiting; (* link waiting processes
- *)
- task: Exec.TaskPtr; (* waiting task
- *)
- END;
-
- VAR
- Processes: Process;
-
- FakeSegList: STRUCT (* needed for OS1.3's CreateProc()
- * (the compiler longword aligns this automatically)
- *)
- length: LONGINT;
- next: Exec.BPTR;
-
-
- jmp: INTEGER; (* JMP StartProcess *)
- start: PROCEDURE;
- END;
-
- ProcessesSemaphore: Exec.SignalSemaphore;
- (* Semaphore to access global variables of this
- * module
- *)
-
- ProcessName: POINTER TO ARRAY 80 OF CHAR;
-
- WakeUpSig: INTEGER;
-
- DefaultStackSize: LONGINT;
-
- ProcessInTrapData: INTEGER; (*
- * Offset of Process in
- * TaskTrapData.user[]
- *)
-
- PROCEDURE ChildHaltProc;
-
- VAR
- p,q: Process;
-
- CONST
- RTS = 4E75H;
-
- BEGIN
- p := SYSTEM.VAL(Process,OberonLib.execBase.thisTask.trapData.user[ProcessInTrapData]);
- Exec.ObtainSemaphore(ProcessesSemaphore);
- Exec.Forbid;
- p.done := TRUE;
- WHILE p.waiting#NIL DO
- Exec.Signal(p.waiting.task,LONGSET{WakeUpSig});
- p.waiting := p.waiting.next;
- END;
- IF p=Processes THEN
- Processes := p.next
- ELSE
- q := Processes;
- WHILE q.next#p DO q := q.next END;
- q.next := p.next;
- END;
- Exec.ReleaseSemaphore(ProcessesSemaphore);
- (* $IF GarbageCollector *)
- GC.RemMutator(p.mutator);
- (* $END *)
- SYSTEM.SETREG(15,OberonLib.execBase.thisTask.trapData.oldSP);
- SYSTEM.INLINE(RTS);
- END ChildHaltProc;
-
-
- PROCEDURE Call(p: Process);
- (*
- * Sets p's priority, calls p.proc and terminates this process.
- *)
- BEGIN
- IF Exec.SetTaskPri(Exec.exec.thisTask,p.priority)=0 THEN END;
- p.result := p.proc(p.data);
- ChildHaltProc;
- END Call;
-
-
- VAR
- StartProcessProc: Process;
-
-
- PROCEDURE StartProc;
- (*
- * adds new mutator to GC and calls Call() to execute this process' procedure.
- *
- * $StackChk-
- *)
-
- BEGIN
- StartProcessProc := SYSTEM.VAL(Process,OberonLib.execBase.thisTask.trapData.user[ProcessInTrapData]);
-
- (* $IF GarbageCollector *)
-
- IF GC.AddMutator(StartProcessProc.mutator) THEN
-
- (* $END *)
-
- Call(StartProcessProc);
-
- (* $IF GarbageCollector *)
-
- ELSE
-
- StartProcessProc.done := TRUE;
- Processes := StartProcessProc.next;
- (* because NewProcessX does a ReleaseSemaphore(ProcessesSemaphore)
- * after it does Permit(), and this processes priority is 127,
- * we are safe to modify the Processes-List.
- *)
-
- END;
-
- (* $END *)
-
- END StartProc;
-
-
- PROCEDURE StartProcess;
- (*
- * Sets A5 and trapData.oldSP
- *
- * $StackChk-
- *)
-
- BEGIN
-
- OberonLib.SetA5;
-
- (* $IF SmallData *)
- OberonLib.execBase.thisTask.trapData.oldSP := SYSTEM.REG(15);
- (* $ELSE *)
- OberonLib.execBase.thisTask.trapData.oldSP := SYSTEM.VAL(LONGINT,SYSTEM.REG(15))-4;
- (* $END *)
-
- StartProc;
-
- END StartProcess;
-
-
- (* ------ NewProcessX: ------ *)
-
-
- PROCEDURE NewProcessX * (proc: ProcessProc;
- data: BT.ANY;
- stackSize: LONGINT;
- priority: SHORTINT): Process;
- (*
- * Start proc as new concurrent process. data will be passed to proc as
- * procedure paramter.
- *
- * proc must be reentrant, ie. it must not access global variables or data
- * structures or call procedures that access global data while other
- * processes may access this data.
- *
- * To be save do not use any global variables within this routine.
- *
- * The new process will have a Stack that is stackSize bytes large. The new
- * process' task priority will be set to priority.
- *
- * This procedure is reentrant and may be called from any process to
- * create new child-processes.
- *
- *)
-
- VAR
- newproc: Process;
- msg: Dos.ProcessId;
-
- BEGIN
-
- NEW(newproc);
-
- newproc.taskTrapData := OberonLib.execBase.thisTask.trapData^;
- newproc.taskTrapData.mutator := SYSTEM.ADR(newproc.mutator);
- newproc.taskTrapData.haltProc := ChildHaltProc;
- newproc.taskTrapData.user[ProcessInTrapData] := SYSTEM.VAL(SYSTEM.ADDRESS,newproc);
- newproc.data := data;
- newproc.proc := proc;
- IF priority=127 THEN DEC(priority) END;
- newproc.priority := priority;
- newproc.done := FALSE;
-
- Exec.ObtainSemaphore(ProcessesSemaphore);
-
- Exec.Forbid;
-
- IF Dos.dos.lib.version<37 THEN
-
- FakeSegList.length := 16;
- FakeSegList.next := NIL;
- FakeSegList.jmp := 4EF9U; (* JMP abslong *)
- FakeSegList.start := StartProcess;
-
- msg := Dos.CreateProc(ProcessName^,
- 127,
- SYSTEM.ADR(FakeSegList.next),
- stackSize);
- IF msg=NIL THEN
- newproc.dosProcess := NIL;
- ELSE
- newproc.dosProcess := SYSTEM.VAL(Dos.ProcessPtr,SYSTEM.VAL(LONGINT,msg)-SIZE(Exec.Task));
- END;
-
- ELSE
-
- newproc.dosProcess := Dos.CreateNewProcTags(
- Dos.npEntry ,SYSTEM.VAL(SYSTEM.ADDRESS,StartProcess),
- Dos.npStackSize,stackSize,
- Dos.npName ,ProcessName,
- Dos.npPriority ,127);
-
- END;
-
- IF newproc.dosProcess#NIL THEN
- newproc.dosProcess.task.trapData := SYSTEM.ADR(newproc.taskTrapData);
- newproc.dosProcess.task.trapCode := Exec.exec.thisTask.trapCode;
- newproc.next := Processes;
- Processes := newproc;
- ELSE
- newproc := NIL;
- END;
-
- Exec.Permit;
-
- (*
- * Because the new process' priority is 127, it will run now. It will add its
- * mutator to the GC and maintain a copy of a pointer to its Process-structure
- * in a local variable within Call(). Then it will set its priority to the
- * specified priority. So we are safe to return now, even if our caller
- * deletes the result, which else would be the only reference to the Process.
- *)
-
- Exec.ReleaseSemaphore(ProcessesSemaphore);
-
- RETURN newproc;
-
- END NewProcessX;
-
-
- (* ------ NewProcess: ------ *)
-
-
- PROCEDURE NewProcess * (proc: ProcessProc;
- data: BT.ANY): Process;
- (*
- * Start proc as new concurrent process. data will be passed to proc as
- * procedure paramter.
- *
- * proc must be reentrant, ie. it must not access global variables or data
- * structures or call procedures that access global data while other
- * processes may access this data.
- *
- * To be save do not use any global variables within this routine.
- *
- * The stackSize and priority will be inherited from this process.
- *
- * This procedure is reentrant and may be called from any process to
- * create new child-processes.
- *)
-
- BEGIN
- RETURN NewProcessX(proc,
- data,
- DefaultStackSize,
- Exec.exec.thisTask.node.pri);
- END NewProcess;
-
-
- (* ------ Wait: ------ *)
-
-
- PROCEDURE (p: Process) Wait * (): BT.ANY;
-
- (*
- * Wait for process p to terminate. Returns its result.
- *)
-
- VAR
- wait: Waiting;
-
- BEGIN
- Exec.ObtainSemaphore(ProcessesSemaphore);
- IF ~ p.done THEN
- NEW(wait);
- wait.task := Exec.exec.thisTask;
- wait.next := p.waiting;
- p.waiting := wait;
- Exec.ReleaseSemaphore(ProcessesSemaphore);
- REPEAT
- UNTIL p.done OR (WakeUpSig IN Exec.Wait(LONGSET{WakeUpSig}));
- ELSE
- Exec.ReleaseSemaphore(ProcessesSemaphore);
- END;
- RETURN p.result;
- END Wait;
-
-
- (* ------ isRunning: ------ *)
-
-
- PROCEDURE (p: Process) isRunning * (): BOOLEAN;
-
- (*
- * Check if p is still running.
- *)
-
- BEGIN
- RETURN ~ p.done
- END isRunning;
-
-
- (* ------ WaitForAllProcesses: ------ *)
-
-
- PROCEDURE WaitForAllProcesses * ;
- (*
- * Wait for all processes to terminate
- *)
- VAR
- p: Process;
- BEGIN
- LOOP
- Exec.ObtainSemaphore(ProcessesSemaphore);
- p := Processes;
- Exec.ReleaseSemaphore(ProcessesSemaphore);
- IF p=NIL THEN EXIT END;
- IF p.Wait()=NIL THEN END;
- END;
- END WaitForAllProcesses;
-
-
- (* ------ Init: ------ *)
-
-
- BEGIN
-
- Exec.InitSemaphore(ProcessesSemaphore);
-
- Processes := NIL;
-
- NEW(ProcessName);
- IF Exec.exec.thisTask.node.name#NIL THEN
- COPY(Exec.exec.thisTask.node.name^,ProcessName^);
- ELSE
- ProcessName^ := "Amiga Oberon";
- END;
-
- ProcessName[55] := 0X; (* Shrink ProcessName if it's too long *)
- Strings.Append(ProcessName^," background process");
-
- DefaultStackSize := OberonLib.OldSP.stackSize;
-
- WakeUpSig := Exec.AllocSignal(-1);
- ProcessInTrapData := OberonLib.AllocUser();
- IF (WakeUpSig<0) OR (ProcessInTrapData<0) THEN
- HALT(20)
- END;
-
- CLOSE
-
- WaitForAllProcesses;
-
- IF WakeUpSig>=0 THEN Exec.FreeSignal(WakeUpSig) END;
- IF ProcessInTrapData>=0 THEN OberonLib.FreeUser(ProcessInTrapData) END;
-
- END Concurrency.
-
-
-
-
-
-
-