-- OurProcess.Mesa Edited by Sandman on June 30, 1980 9:27 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY ControlDefs USING [Frame, FrameHandle, NullFrame, StateVector], FrameOps USING [Free, MyLocalFrame, SetReturnFrame], InlineDefs USING [BITOR, BITSHIFT, HighHalf, LowHalf], NucleusOps USING [], Process USING [], ProcessDefs USING [ DefaultPriority, DefaultTimeout, DisableInterrupts, DIW, EnableInterrupts, Priority, Ticks, TimeoutLevel], ProcessOps USING [ ActiveWord, Broadcast, Clean, CurrentPSB, CurrentState, EnableAndRequeue, Enter, Exit, FirstProcess, FirstStateVector, LastProcess, Notify, NullQueueHandle, ReadyList, ReEnter, TimerGrain, Wait], PSBDefs USING [ Condition, Empty, MonitorLock, ProcessHandle, PSB, UnlockedEmpty], SDDefs USING [SD, sFork, sJoin, sProcessTrap]; OurProcess: MONITOR LOCKS Processes IMPORTS FrameOps, InlineDefs, ProcessDefs, ProcessOps EXPORTS NucleusOps, ProcessDefs, Process = BEGIN OPEN ProcessDefs, ProcessOps, PSBDefs; PSBBase: CARDINAL = 0; TimedOut: PUBLIC SIGNAL = CODE; TooManyProcesses: PUBLIC ERROR = CODE; Processes: MONITORLOCK; frameReady, frameTaken, dead, rebirth: CONDITION; DyingFrameHandle: TYPE = POINTER TO dying ControlDefs.Frame; Fork: PUBLIC PROCEDURE [root: UNSPECIFIED] RETURNS [ProcessHandle] = BEGIN sv: ControlDefs.StateVector; self: ControlDefs.FrameHandle; Forker: PROCEDURE [ProcessHandle]; newPSB: ProcessHandle; sv ← STATE; WHILE ~Enter[@Processes] DO NULL ENDLOOP; self ← FrameOps.MyLocalFrame[]; Forker ← LOOPHOLE[self.returnlink]; IF LOOPHOLE[rebirth, Condition].queue = Empty THEN BEGIN Exit[@Processes]; ERROR TooManyProcesses; END; newPSB ← (PSBBase + LOOPHOLE[rebirth, Condition].queue).link; newPSB↑ ← PSB[ link: newPSB.link, cleanup: Clean, timeout: 0, priority: CurrentPSB.priority, enterFailed: FALSE, detached: FALSE, fill: 0, state: dead, -- in case of timeout before Notify (below) timeoutAllowed: TRUE, abortPending: FALSE, timeoutPending: FALSE, waitingOnCV: TRUE, frame: self]; Notify[@rebirth]; -- wake up newPSB, and set alive; DEPENDS newPSB.state ← alive; -- on new process not preempting parent... FrameOps.SetReturnFrame[ControlDefs.NullFrame]; Forker[newPSB]; -- "returns" handle to site of FORK -- Note that the lines above are executed by the forking process, while -- the lines below are executed by the forked process. Note also that the -- monitor remains LOCKED during this fancy footwork...! sv.dest ← root; sv.source ← End; Exit[@Processes]; RETURN WITH sv END; deadFrame: DyingFrameHandle ← NIL; -- only for detached processes End: PROCEDURE = BEGIN OPEN p: CurrentPSB↑; sv: ControlDefs.StateVector; frame: DyingFrameHandle; sv ← STATE; WHILE ~Enter[@Processes] DO NULL ENDLOOP; frame ← LOOPHOLE[FrameOps.MyLocalFrame[]]; frame.state ← alive; p.state ← frameReady; p.abortPending ← FALSE; -- too late for Aborts: they no-op Broadcast[@frameReady]; UNTIL p.state = frameTaken OR p.detached DO Wait[@Processes, @frameTaken, LOOPHOLE[frameTaken, Condition].timeout]; WHILE ~ReEnter[@Processes, @frameTaken] DO NULL ENDLOOP; ENDLOOP; IF deadFrame # NIL THEN BEGIN FrameOps.Free[deadFrame]; deadFrame ← NIL END; IF p.detached THEN deadFrame ← frame; -- Leave our frame for freeing frame.state ← dead; p.state ← dead; Broadcast[@dead]; Wait[@Processes, @rebirth, LOOPHOLE[rebirth, Condition].timeout]; WHILE ~ReEnter[@Processes, @rebirth] DO NULL ENDLOOP; -- dying process exits here; JOINing process does below. sv.dest ← frame.returnlink; -- set to site of JOIN by Join sv.source ← 0; Exit[@Processes]; RETURN WITH sv; END; Join: PUBLIC ENTRY PROCEDURE [process: UNSPECIFIED] RETURNS [ControlDefs.FrameHandle] = BEGIN p: ProcessHandle = process; frame: DyingFrameHandle; self: ControlDefs.FrameHandle = FrameOps.MyLocalFrame[]; ValidateProcess[p]; WHILE p.state # frameReady DO WAIT frameReady ENDLOOP; -- guaranteed to be a dying frame by the time we get here frame ← LOOPHOLE[p.frame]; p.state ← frameTaken; BROADCAST frameTaken; WHILE frame.state # dead DO WAIT dead ENDLOOP; frame.returnlink ← self.returnlink; -- site of JOIN RETURN[frame] END; Detach: PUBLIC ENTRY PROCEDURE [process: UNSPECIFIED] = BEGIN p: ProcessHandle = process; ValidateProcess[p]; p.detached ← TRUE; BROADCAST frameTaken; END; Abort: PUBLIC PROCEDURE [process: UNSPECIFIED] = BEGIN p: ProcessHandle = process; ValidateProcess[p]; DisableInterrupts[]; IF p.state = alive THEN BEGIN p.abortPending ← TRUE; IF p.waitingOnCV THEN BEGIN p.waitingOnCV ← FALSE; EnableAndRequeue[NullQueueHandle, ReadyList, p]; RETURN; END; END; EnableInterrupts[]; END; ProcessTrap: PROCEDURE RETURNS [BOOLEAN] = BEGIN abort, timeout: BOOLEAN; CurrentPSB.waitingOnCV ← FALSE; abort ← CurrentPSB.abortPending; CurrentPSB.abortPending ← FALSE; IF abort THEN ERROR ABORTED; timeout ← CurrentPSB.timeoutPending; CurrentPSB.timeoutPending ← FALSE; IF timeout THEN SIGNAL TimedOut; RETURN[TRUE] END; Pause: PUBLIC ENTRY PROCEDURE [ticks: Ticks] = BEGIN condition: CONDITION; SetTimeout[@condition, ticks]; WAIT condition; END; Yield: PUBLIC PROCEDURE = BEGIN DisableInterrupts[]; EnableAndRequeue[ReadyList, ReadyList, CurrentPSB↑]; RETURN END; GetCurrent: PUBLIC PROCEDURE RETURNS [UNSPECIFIED] = BEGIN RETURN[CurrentPSB↑] END; GetPriority: PUBLIC PROCEDURE RETURNS [p: Priority] = BEGIN DisableInterrupts[]; p ← CurrentPSB.priority; EnableInterrupts[]; RETURN END; SetPriority: PUBLIC PROCEDURE [p: Priority] = BEGIN DisableInterrupts[]; CurrentPSB.priority ← p; EnableAndRequeue[ReadyList, ReadyList, CurrentPSB↑]; END; SetTimeout: PUBLIC PROCEDURE [ condition: POINTER TO CONDITION, ticks: CARDINAL] = BEGIN LOOPHOLE[condition, POINTER TO Condition].timeout ← IF ticks # 0 THEN ticks ELSE DefaultTimeout; RETURN END; DisableTimeout: PUBLIC PROCEDURE [condition: POINTER TO CONDITION] = BEGIN LOOPHOLE[condition, POINTER TO Condition].timeout ← 0; RETURN END; SecondsToTicks: PUBLIC PROCEDURE [sec: CARDINAL] RETURNS [Ticks] = BEGIN OPEN InlineDefs; ticks: LONG INTEGER; ticks ← (LONG[sec]*LONG[1000] + TimerGrain - 1)/TimerGrain; RETURN[IF HighHalf[ticks] # 0 THEN LAST[Ticks] ELSE LowHalf[ticks]]; END; MsecToTicks: PUBLIC PROCEDURE [ms: CARDINAL] RETURNS [Ticks] = BEGIN RETURN[(ms + TimerGrain - 1)/TimerGrain] END; TicksToMsec: PUBLIC PROCEDURE [ticks: Ticks] RETURNS [CARDINAL] = BEGIN RETURN[ticks*TimerGrain] END; InitializeMonitor: PUBLIC PROCEDURE [monitor: POINTER TO MONITORLOCK] = BEGIN LOOPHOLE[monitor, POINTER TO MonitorLock]↑ ← UnlockedEmpty; RETURN END; InitializeCondition: PUBLIC PROCEDURE [ condition: POINTER TO CONDITION, ticks: CARDINAL] = BEGIN LOOPHOLE[condition, POINTER TO Condition]↑ ← Condition[no, Empty, ticks]; RETURN END; ValidateProcess: PUBLIC PROCEDURE [p: ProcessHandle] = BEGIN c: CARDINAL = LOOPHOLE[p]; IF c < LOOPHOLE[FirstProcess↑, CARDINAL] OR c > LOOPHOLE[LastProcess↑, CARDINAL] OR (c - LOOPHOLE[FirstProcess↑, CARDINAL]) MOD SIZE[PSB] # 0 THEN SIGNAL InvalidProcess[p]; RETURN END; InvalidProcess: PUBLIC SIGNAL [process: ProcessHandle] = CODE; InitializeProcesses: PROCEDURE = BEGIN OPEN SDDefs; sd: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD; firstPSB: ProcessHandle ← FirstProcess↑; lastPSB: ProcessHandle ← LastProcess↑; psb: ProcessHandle; DisableTimeout[@dead]; DisableTimeout[@frameReady]; DisableTimeout[@frameTaken]; DisableTimeout[@rebirth]; CurrentState↑ ← FirstStateVector↑ + DefaultPriority*SIZE[ControlDefs.StateVector]; -- locate and initialize PSBs sd[sProcessTrap] ← ProcessTrap; sd[sFork] ← Fork; sd[sJoin] ← Join; -- fabricate PSB for self lastPSB↑ ← PSB[ link: lastPSB, cleanup: Clean, timeout: 0, enterFailed: FALSE, detached: FALSE, fill: 0B, state: alive, timeoutAllowed:, abortPending: FALSE, timeoutPending: FALSE, waitingOnCV: FALSE, priority: DefaultPriority, frame: FrameOps.MyLocalFrame[]]; CurrentPSB↑ ← ReadyList↑ ← lastPSB; -- set up free PSB pool ("rebirth" condition) FOR psb ← firstPSB, psb + SIZE[PSB] UNTIL psb = lastPSB DO psb↑ ← PSB[ link: psb - SIZE[PSB], cleanup: Clean, timeout: 0, enterFailed: FALSE, detached: FALSE, fill: 0B, state: dead, timeoutAllowed:, abortPending: FALSE, timeoutPending: FALSE, waitingOnCV: TRUE, priority: DefaultPriority, frame: ControlDefs.NullFrame]; ENDLOOP; LOOPHOLE[rebirth, Condition].queue ← (firstPSB.link ← lastPSB - SIZE[PSB]) - PSBBase; -- CV↑ already set up by Nova code ActiveWord↑ ← 77777B; RETURN END; InitializeTimeouts: PROCEDURE = BEGIN OPEN InlineDefs; TimeoutMask: WORD = BITSHIFT[1, TimeoutLevel]; DisableInterrupts[]; -- Nova code has set up IntVec[TimeoutLevel] DIW↑ ← BITOR[DIW↑, TimeoutMask]; EnableInterrupts[]; RETURN END; InitializeProcesses[]; InitializeTimeouts[]; END.