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