-- MMProcess.Mesa  Edited by Sandman on May 18, 1979  10:17 AM
-- Edited by Forrest on July 15, 1980  2:48 PM
-- Last modified October 18, 1983  4:36 PM by Taft

DIRECTORY
  ControlDefs USING [
    Frame, FrameHandle, NullFrame, StateVector],
  FrameOps USING [Free, MyLocalFrame, SetReturnFrame],
  InlineDefs USING [HighHalf, LowHalf],
  MMInit,
  ProcessDefs USING [
    DefaultTimeout, DisableInterrupts, EnableInterrupts,
    Priority, Ticks],
  ProcessOps USING [
    Broadcast, Clean, CurrentPSB, EnableAndRequeue, Enter, Exit, FirstProcess,
    LastProcess, Notify, NullQueueHandle, ReadyList, ReEnter,
    TimerGrain, Wait],
  PSBDefs USING [
    Condition, Empty, MonitorLock, ProcessHandle, PSB, UnlockedEmpty];

MMProcess: MONITOR LOCKS Processes
  IMPORTS FrameOps, InlineDefs, ProcessDefs, ProcessOps
  EXPORTS MMInit, ProcessDefs, ProcessOps =
  BEGIN OPEN ProcessDefs, ProcessOps, PSBDefs;

  PSBBase: CARDINAL = 0;

  TimedOut: PUBLIC SIGNAL = CODE;
  TooManyProcesses: PUBLIC ERROR = CODE;

  Processes: MONITORLOCK;
  frameReady, frameTaken, dead, rebirth: PUBLIC 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: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    abort: BOOLEAN;
    CurrentPSB.waitingOnCV ← FALSE;
    abort ← CurrentPSB.abortPending;
    CurrentPSB.abortPending ← FALSE;
    ERROR ABORTED;
    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;

  END.