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