-- 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.