-- RTStopProcessImpl.Mesa
-- last edited September 9, 1982 7:49 am by Paul Rovner


DIRECTORY
Process USING[Abort, Priority, priorityForeground],
ProcessOperations USING[IndexToHandle],
PSB USING[PDA],
RTBasic USING[TV],
RTProcess USING[Status, Valid, InvalidProcess, HandleToPSBI],
RTProcessPrivate USING[ProcessObject];

RTStopProcessImpl: MONITOR LOCKS h USING h: Handle
IMPORTS Process, ProcessOperations, RTProcess
EXPORTS RTProcess

= BEGIN OPEN RTProcess;

-- types
Handle: TYPE = REF Object;
Object: PUBLIC TYPE = RTProcessPrivate.ProcessObject;


-- SIGNALs
NotStopped: PUBLIC SIGNAL[h: Handle] = CODE;
InvalidPriority: PUBLIC SIGNAL[h: Handle, p: Process.Priority] = CODE;
NIY: SIGNAL = CODE;

-- PROCs

GetStatus: PUBLIC PROC[h: Handle] RETURNS[RTProcess.Status] =
{ IF NOT Valid[h] THEN RETURN[invalid];
IF h.stopState = stopped THEN RETURN[stopped];
ERROR NIY
};

Stop: PUBLIC PROC[h: Handle] =
{ IF NOT Valid[h] THEN ERROR InvalidProcess[h];

IF h.stopState # stoppable THEN RETURN;

-- Wait for h's priority to be <= Process.priorityForeground, then grab it with the Sponge.

-- Scan h's call stack until a client module is found, find the next clean point there and place a client BP

h.stopState ← stopping;

-- release the sponge

-- NOTE: Some time later, when the client BP is hit by the client process, remove the BP, h.stopState ← stopped, and cause the client process to WAIT h.resume.

};

Proceed: PUBLIC ENTRY PROC[h: Handle] =
{ IF NOT Valid[h] THEN ERROR InvalidProcess[h];
IF h.stopState = stopped
THEN {h.stopState ← stoppable; NOTIFY h.resume}
ELSE IF h.stopState = stopping
THEN {h.stopState ← stoppable;
-- remove the BP
  }
ELSE SIGNAL NotStopped[h];
};

-- raises NotStopped
ReturnFrom: PUBLIC ENTRY PROC[h: Handle, frameFromWhichToReturn: RTBasic.TV, result: RTBasic.TVNIL] =
{ IF NOT Valid[h] THEN ERROR InvalidProcess[h];
IF h.stopState = stopped
THEN {h.stopState ← unwinding;
--remember "frameFromWhichToReturn" and "result";
  Process.Abort[HandleToPSBI[h]]} -- NOTE 1, NOTE 3
ELSE SIGNAL NotStopped[h]; -- NOTE 2
};

-- raises NotStopped
Abort: PUBLIC ENTRY PROC[h: Handle] =
{ IF NOT Valid[h] THEN ERROR InvalidProcess[h];
IF h.stopState = stopped
THEN {h.stopState ← aborting;
Process.Abort[HandleToPSBI[h]]} -- NOTE 1, NOTE 3
ELSE IF h.stopState = stopping --and h is waiting on a CV
THEN {--remove the BP;
Process.Abort[HandleToPSBI[h]]}
ELSE SIGNAL NotStopped[h]; -- NOTE 2
};

-- raises NotStopped
TopFrame: PUBLIC PROC[h: Handle] RETURNS[RTBasic.TV--for a frame--] =
{ IF NOT Valid[h] THEN ERROR InvalidProcess[h];
IF h.stopState = stopped
THEN RETURN[NIL--find h's top client frame (skip back over BP handler frames)--]
ELSE IF h.stopState = stopping
THEN ERROR
ELSE ERROR NotStopped[h];
};

-- raises NotStopped, InvalidPriority, InvalidProcess
ChangePriority: PUBLIC PROC[h: Handle, new: Process.Priority]
RETURNS[old: Process.Priority] =
{ IF NOT Valid[h] THEN ERROR InvalidProcess[h];
IF new > Process.priorityForeground THEN ERROR InvalidPriority[h, new];
old ← PSB.PDA[ProcessOperations.IndexToHandle[HandleToPSBI[h]]].link.priority;
IF h.stopState = stopped
THEN SIGNAL NIY -- NOTE change h's priority: must its PSB be moved among system queues?
ELSE SIGNAL NotStopped[h]; -- NOTE 2
};

-- NOTE 1: Current catch phrases in the frame where the client BP was placed will not be executed.
-- In particular, ENABLE UNWIND => NULL in an ENTRY frame will not be executed, hence the monitor
-- lock will not be released. This will be fixed when the new instruction set is installed.

-- NOTE 2: The process may not have hit the BP yet, especially if it is still WAITing on a
-- CV or to enter a MONITOR.

-- NOTE 3: A catch phrase for ABORT in the stopper will
--  IF h.stopState = unwinding
--  THEN unwind frames through "frameFromWhichToReturn";
--  h.stopState ← stoppable;
--  fix the PSB (which was marked "aborted");
--  stuff "result" into a result record and return to the caller of "frameFromWhichToReturn"
--  ELSE IF h.stopState = aborting
--  THEN NULL
--  ELSE ERROR;

END.