-- RTProcessPrivateImpl.Mesa
-- last edited February 22, 1983 1:27 pm by Paul Rovner

DIRECTORY
AMProcessBasic USING[LocalLink],
Environment USING[Long, wordsPerPage],
Inline USING[LongCOPY],
PrincOps USING [FrameHandle, GlobalFrameHandle, Frame, LargeReturnSlot,
FrameVec, NullFrame, stackDepth, ControlLink],
Process USING[Priority, SetPriority, GetPriority, ValidateProcess, InvalidProcess],
ProcessInternal USING[DisableInterrupts],
ProcessOperations USING[IndexToHandle, HandleToIndex, ReadPSB,
EnableAndRequeue],
PSB USING[PsbHandle, PsbIndex, PDA, StateVectorHandle],
RTBasic USING [Address],
RTProcessPrivate USING [NewCedarProcessRegistered, InvalidateCedarProcess],
RTCommon USING [ShortenLongCardinal],
RTOS USING[GetCurrent, MyLocalFrame, End],
RTSD USING[SD, sFreezer],
RTSponge USING[spongePriority, StartSponge, StopSponge],
Runtime USING[GlobalFrame],
Space USING[nullHandle, PageNumber, Create, virtualMemory, Handle,
Delete, Map, LongPointer, Kill],
SpecialSpace USING[MakeResident, MakeSwappable, MakeCodeResident, MakeCodeSwappable,
MakeGlobalFrameResident--, MakeGlobalFrameSwappable--];

RTProcessPrivateImpl: MONITOR -- Protects the Cedar process registry
IMPORTS AMProcessBasic, Inline, Process, ProcessInternal, ProcessOperations,
RTCommon, RTOS, RTProcessPrivate, RTSponge, Runtime, Space, SpecialSpace
EXPORTS RTOS
= BEGIN


PageSize: CARDINAL = Environment.wordsPerPage;

--Public signals
FrameCopySpaceTooSmall: PUBLIC SIGNAL = CODE;
TooManyCedarProcesses: PUBLIC SIGNAL = CODE;

-- Variables
-- pinned frame body buffer for the stack scan
fbpNext: LONG POINTERNIL;
fbp: LONG POINTERNIL; -- init by a call on AllocateFrameBodyBuffer from RTRefCountsImpl
fbpMaxNext: LONG POINTER;
codeLockCount: CARDINAL ← 0;
rtProcessStarted: BOOLFALSE;
frameBodyMasterPages: CARDINAL ← 60;
frameBodyMasterSpace: Space.Handle ← Space.nullHandle;
frameBodyBufferSpace: Space.Handle ← Space.nullHandle;

thisModule: PROGRAM = Runtime.GlobalFrame[RegisterCedarProcess];

-- NOTE resident
CedarProcessRegistry: PACKED ARRAY PSB.PsbIndex OF BOOLALL[FALSE];

nCedarProcesses: CARDINAL ← 0;

maxCedarPSBI: PSB.PsbIndex ← 0;

CedarFrameChainRegistry: ARRAY [0..CedarFrameChainRegistrySize) OF DESCRIPTOR FOR ARRAY OF POINTER TO PrincOps.FrameHandle;
CedarFrameChainRegistrySize: CARDINAL = 20;
fchNext: CARDINAL ← 0;


-- ************ Process registry maintenance (Begin)

UnregisterCedarProcess: PUBLIC PROC[psbi: PSB.PsbIndex] =
{ IF DoUnregisterCedarProcess[psbi] AND rtProcessStarted
THEN RTProcessPrivate.InvalidateCedarProcess[psbi]};

DoUnregisterCedarProcess: ENTRY PROC[psbi: PSB.PsbIndex]
RETURNS[deregistered: BOOL] =
{ ENABLE UNWIND => NULL;
IF CedarProcessRegistry[psbi]
THEN {CedarProcessRegistry[psbi] ← FALSE;
IF psbi = maxCedarPSBI THEN maxCedarPSBI ← maxCedarPSBI - 1;
nCedarProcesses ← nCedarProcesses - 1;
RETURN[TRUE]}
ELSE RETURN[FALSE]};

RegisterCedarProcess: PUBLIC PROC[psbi: PSB.PsbIndex] =
{IF DoRegisterCedarProcess[psbi].found THEN RETURN;
IF rtProcessStarted THEN RTProcessPrivate.NewCedarProcessRegistered[psbi]};

RealCL: INTERNAL PROC[cl: PrincOps.ControlLink, psbi: PSB.PsbIndex]
RETURNS[PrincOps.ControlLink] =
INLINE
{IF RTSD.SD[RTSD.sFreezer] # 0
THEN RETURN[AMProcessBasic.LocalLink[cl, psbi]]
ELSE RETURN[cl]};

DoRegisterCedarProcess: ENTRY PROC[psbi: PSB.PsbIndex] RETURNS[found: BOOL] =
{ ENABLE UNWIND => NULL;
IF CedarProcessRegistry[psbi] THEN RETURN[TRUE]; -- awreddy registered.

-- whiz up the dynamic call chain until I find the top
-- and stuff RTOS.End there
FOR root: PrincOps.FrameHandle ← RTOS.MyLocalFrame[], RealCL[root.returnlink, psbi].frame
DO IF root.returnlink.proc THEN {root.returnlink ← LOOPHOLE[RTOS.End]; EXIT} ENDLOOP;

-- now register it.
CedarProcessRegistry[psbi] ← TRUE;
IF psbi > maxCedarPSBI THEN maxCedarPSBI ← psbi;
nCedarProcesses ← nCedarProcesses + 1;
RETURN[FALSE]};

RegisterFrameChains: PUBLIC ENTRY PROC[d: DESCRIPTOR FOR ARRAY OF POINTER TO PrincOps.FrameHandle] =
{ ENABLE UNWIND => NULL;
FOR i: CARDINAL IN [0..fchNext)
DO IF CedarFrameChainRegistry[i] = DESCRIPTOR[NIL, 0]
THEN {CedarFrameChainRegistry[i] ← d; RETURN}; -- found an empty slot
ENDLOOP;

-- not enough slots; here to extend the registry (someday)
IF fchNext > CedarFrameChainRegistrySize THEN ERROR; -- ERROR TooManyCedarFrameChains;

CedarFrameChainRegistry[fchNext] ← d;
fchNext ← fchNext + 1};

UnregisterFrameChains: PUBLIC ENTRY PROC[d: DESCRIPTOR FOR ARRAY OF POINTER TO PrincOps.FrameHandle] =
{ ENABLE UNWIND => NULL;
FOR i: CARDINAL IN [0..fchNext)
DO IF CedarFrameChainRegistry[i] = d
THEN {CedarFrameChainRegistry[i] ← DESCRIPTOR[NIL, 0]; RETURN}; -- found it
ENDLOOP;
ERROR};


-- ************ Process registry maintenance (End)


--**************Stoppable process stuff (Begin)

-- After this returns there will be no cedar client activity until ReleaseProcesses returns
-- StopProcesses can't use FrameImpl for its return
-- (i.e it must be an inline), because someone at a
-- lower priority may be in FrameImpl, holding the lock
StopProcesses: PROC RETURNS[oldPriority: Process.Priority] =
INLINE
{ oldPriority ← Process.GetPriority[]; -- remember the caller's priority
IF oldPriority >= RTSponge.spongePriority THEN ERROR;
Process.SetPriority[RTSponge.spongePriority];
RTSponge.StartSponge[];
RETURN[oldPriority]};

Stopped: PROC[ph: PSB.PsbHandle] RETURNS[BOOL] =
INLINE
{RETURN[PSB.PDA[ph].link.priority < RTSponge.spongePriority]};

ReleaseProcesses: PROC[oldPriority: Process.Priority] =
INLINE
{ -- allow cedar client processes to continue
RTSponge.StopSponge[];
Process.SetPriority[oldPriority]};

--**************Stoppable process stuff (End)


-- Collector invariant guarantees that only one guy at a time will be in here.
SnapshotTheFrameHeap: PUBLIC ENTRY PROC =
{ ENABLE UNWIND => NULL;
myPriority: Process.Priority;
success: BOOL;

SpecialSpace.MakeResident[frameBodyBufferSpace];
fbpNext ← fbp;
-- If fbp storage is too small, FrameCopySpaceTooSmall will be raised AFTER
-- ReleaseProcesses is done
LockThisCode[];
myPriority ← StopProcesses[]; -- an INLINE. No use of FrameImpl for return, cause
-- someone at a lower priority may be in it, holding its lock.
success ← DoSnapshotTheFrameHeap[];
ReleaseProcesses[myPriority];
UnLockThisCode[];
IF NOT success
THEN {Space.Kill[frameBodyBufferSpace];
SpecialSpace.MakeSwappable[frameBodyBufferSpace];
ERROR FrameCopySpaceTooSmall};
SpecialSpace.MakeSwappable[frameBodyBufferSpace]};

NewDoSnapshotTheFrameHeap: INTERNAL PROC RETURNS[success: BOOL ← TRUE] = INLINE {
-- turn off interrupts
-- copy state vector bodies
-- for each state vector
-- { svh: PSB.StateVectorHandle = XXX;
-- IF NOT CopyFrameBody[LOOPHOLE[@PSB.PDA[svh].stk, LONG POINTER],
-- MIN[PSB.PDA[svh].stkptr + 1, PrincOps.stackDepth]]
-- ....stkptr + 1 because one word of a REF may have been popped
-- THEN {turn on interrupts; RETURN[FALSE]}};
-- initialize framePageMap
-- FramePageMapArray: TYPE = ARRAY [0..256) OF LONG POINTER;
-- framePageMap: LONG POINTER TO FramePageMapArray ← fbpNext;
-- RTMicrocode.LONGZERO[framePageMap, SIZE[FramePageMapArray]];
-- fbpNextfbpNext + SIZE[FramePageMapArray];
-- copy the frame heap, building a map of MDS page # -> frameBodyBuffer offset
-- for each frame heap page ...
-- { framePage: POINTER = XXX;
-- framePageMap[framePage/Environment.wordsPerPage] ← fbpNext
-- IF NOT CopyFrameBody[LONG[framePage], Environment.wordsPerPage]
-- THEN {turn on interrupts; RETURN[FALSE]}};
-- copy the frame free list headers
-- freeListHeaders ← fbpNext
-- IF NOT CopyFrameBody[XXX, XXX]
-- THEN {turn on interrupts; RETURN[FALSE]};
-- turn on interrupts
-- for each frame free list, whiz thru corresponding copied frames, clearing their bodies to 0
-- for each frame free list (flp)
-- FOR f: LONG POINTER TO PrincOps.Frame
-- ← framePageMap[flp/Environment.wordsPerPage]
-- + flp MOD Environment.wordsPerPage
-- , framePageMap[f.XXX/Environment.wordsPerPage]
-- + f.XXX MOD Environment.wordsPerPage
-- UNTIL XXX
-- DO
-- RTMicrocode.LONGZERO[@f.local, GetXFrameSize[f]-SIZE[local PrincOps.Frame]];
-- ENDLOOP;
-- (OPTIONAL) whiz thru the GFT. For each entry, if it points into the frame heap, clear
-- that frame's body to 0.
-- Parse the frame heap: clear frame headers to 0 while (OPTIONALLY) compactifying the
-- frameBodyBuffer
};

GetXFrameSize: PROC[f: LONG POINTER TO PrincOps.Frame] RETURNS [CARDINAL] =
INLINE
{ FramePrefix: TYPE = MACHINE DEPENDENT RECORD[size: CARDINAL, fsi: CARDINAL];
prefix: LONG POINTER TO FramePrefix = LOOPHOLE[f - SIZE[FramePrefix]];
RETURN[IF prefix.fsi = PrincOps.LargeReturnSlot
THEN prefix.size - 1 -- the fsi word is included in the size
ELSE PrincOps.FrameVec[prefix.fsi]]};

DoSnapshotTheFrameHeap: INTERNAL PROC RETURNS[success: BOOL] = INLINE
{ myPSBI: PSB.PsbIndex = RTOS.GetCurrent[];
FOR i: CARDINAL IN [0..fchNext)
DO FOR j: CARDINAL IN [0..LENGTH[CedarFrameChainRegistry[i]])
DO IF NOT WalkForCopy[CedarFrameChainRegistry[i][j]^, myPSBI] -- NOTE XXX ARGH
THEN RETURN[FALSE]
ENDLOOP;
ENDLOOP;
FOR psbi: PSB.PsbIndex IN [1..maxCedarPSBI]
DO IF CedarProcessRegistry[psbi] THEN
{ ph: PSB.PsbHandle = ProcessOperations.IndexToHandle[psbi];
f: PrincOps.FrameHandle ← NIL;
IF myPSBI # psbi
THEN UNTIL Stopped[ph] DO Yield[] ENDLOOP;
-- here with control, which is retained AT THIS PRIORITY til end of FOR iteration
IF myPSBI = psbi
THEN f ← RealCL[RTOS.MyLocalFrame[].returnlink, myPSBI].frame
-- caller of SnapshotTheFrameHeap
ELSE IF PSB.PDA[ph].link.vector
THEN -- there's a state vector here
{ svh: PSB.StateVectorHandle = PSB.PDA[ph].context.state;
IF NOT CopyFrameBody[LOOPHOLE[@PSB.PDA[svh].stk,
LONG POINTER TO RTBasic.Address],
MIN[PSB.PDA[svh].stkptr + 1, PrincOps.stackDepth]]
THEN RETURN[FALSE]; -- + 1: one word of a REF may have been popped
f ← PSB.PDA[svh].frame}
ELSE f ← PSB.PDA[ph].context.frame;
IF NOT WalkForCopy[f, psbi] THEN RETURN[FALSE];
};-- end IF CedarProcessRegistry[psbi] THEN
ENDLOOP;
RETURN[TRUE]};

CheckForModuleReplacement: PUBLIC ENTRY PROC[gfh: PrincOps.GlobalFrameHandle]
RETURNS[ok: BOOL] =
{ ENABLE UNWIND => NULL;
myPriority: Process.Priority;
IF gfh.copied OR gfh.shared THEN RETURN[FALSE];
LockThisCode[];
myPriority ← StopProcesses[]; -- an INLINE. No use of FrameImpl for return
ok ← LookForGFH[gfh];
ReleaseProcesses[myPriority];
UnLockThisCode[]};

WalkForCopy: INTERNAL PROC[f: PrincOps.FrameHandle, psbi: PSB.PsbIndex]
RETURNS[success: BOOLTRUE] =
INLINE
{WHILE ValidFrame[f] DO
IF NOT CopyFrameBody[LOOPHOLE[LONG[@f.local]],
GetFrameSize[f]-SIZE[local PrincOps.Frame]]
THEN RETURN[FALSE];
f ← RealCL[f.returnlink, psbi].frame;
ENDLOOP};

WalkForLook: INTERNAL PROC[f: PrincOps.FrameHandle,
gfh: PrincOps.GlobalFrameHandle,
psbi: PSB.PsbIndex]
RETURNS[found: BOOLFALSE] =
INLINE
{WHILE ValidFrame[f]
DO IF f.accesslink = gfh THEN RETURN[TRUE];
f ← RealCL[f.returnlink, psbi].frame;
ENDLOOP};

LookForGFH: INTERNAL PROC[gfh: PrincOps.GlobalFrameHandle]
RETURNS[notFound: BOOLTRUE] =
INLINE
{ myPSBI: PSB.PsbIndex = RTOS.GetCurrent[];
FOR i: CARDINAL IN [0..fchNext)
DO FOR j: CARDINAL IN [0..LENGTH[CedarFrameChainRegistry[i]])
DO IF WalkForLook[CedarFrameChainRegistry[i][j]^, gfh, myPSBI] -- NOTE XXX ARGH
THEN RETURN[FALSE]
ENDLOOP;
ENDLOOP;
FOR psbi: PSB.PsbIndex IN [1..maxCedarPSBI]
DO IF CedarProcessRegistry[psbi] THEN
{ ph: PSB.PsbHandle = ProcessOperations.IndexToHandle[psbi];
f: PrincOps.FrameHandle ← NIL;
IF myPSBI # psbi
THEN UNTIL Stopped[ph] DO Yield[] ENDLOOP;
-- here with control, which is retained AT THIS PRIORITY til end of FOR iteration
IF myPSBI = psbi
THEN f ← RealCL[RTOS.MyLocalFrame[].returnlink, myPSBI].frame
-- caller of SnapshotTheFrameHeap
ELSE IF PSB.PDA[ph].link.vector
THEN -- there's a state vector here
{ svh: PSB.StateVectorHandle = PSB.PDA[ph].context.state;
f ← PSB.PDA[svh].frame}
ELSE f ← PSB.PDA[ph].context.frame;
IF WalkForLook[f, gfh, psbi] THEN RETURN[FALSE]};
ENDLOOP};

LockThisCode: INTERNAL PROC =
INLINE
{SpecialSpace.MakeGlobalFrameResident[RTProcessPrivateImpl];
SpecialSpace.MakeCodeResident[thisModule]};

UnLockThisCode: INTERNAL PROC =
INLINE
{SpecialSpace.MakeCodeSwappable[thisModule];
--SpecialSpace.MakeGlobalFrameSwappable[RTProcessPrivateImpl]--};

MapRCFrameBodies: PUBLIC PROC[conservativeScanner:
PROC[pa: LONG POINTER TO RTBasic.Address,
nWords: CARDINAL]] =
{conservativeScanner
[fbp,
RTCommon.ShortenLongCardinal[LOOPHOLE[fbpNext, LONG CARDINAL]
- LOOPHOLE[fbp, LONG CARDINAL]]];
Space.Kill[frameBodyBufferSpace];
};

-- called by the collector: only one guy at a time can be in
-- here, and exclusive of SnapshotTheFrameHeap
AllocateFrameBodyBuffer: PUBLIC PROC[nPages: CARDINAL] =
{ IF frameBodyBufferSpace # Space.nullHandle THEN Space.Delete[frameBodyBufferSpace];
IF nPages > frameBodyMasterPages THEN
{ frameBodyMasterPages ← nPages;
Space.Delete[frameBodyMasterSpace];
frameBodyMasterSpace ← Space.Create[size: frameBodyMasterPages,
parent: Space.virtualMemory]};

frameBodyBufferSpace ← Space.Create[nPages, frameBodyMasterSpace];
Space.Map[frameBodyBufferSpace];
fbp ← fbpNext ← Space.LongPointer[frameBodyBufferSpace];
fbpMaxNext ← fbp + nPages * PageSize - 1};

CopyFrameBody: INTERNAL PROC[pa: LONG POINTER, nWords: CARDINAL]
RETURNS[success: BOOL] =
INLINE
{ dest: LONG POINTER = fbpNext;
fbpNext ← fbpNext + nWords;
IF LOOPHOLE[fbpNext, LONG CARDINAL] > LOOPHOLE[fbpMaxNext, LONG CARDINAL]
THEN RETURN[FALSE]
ELSE {Inline.LongCOPY[pa, nWords, dest]; RETURN[TRUE]}};

GetFrameSize: PROC[f: PrincOps.FrameHandle] RETURNS [CARDINAL] =
INLINE
{ FramePrefix: TYPE = MACHINE DEPENDENT RECORD[ size: CARDINAL, fsi: CARDINAL];
prefix: POINTER TO FramePrefix = LOOPHOLE[f - SIZE[FramePrefix]];
RETURN[IF prefix.fsi = PrincOps.LargeReturnSlot
THEN prefix.size - 1 -- the fsi word is included in the size
ELSE PrincOps.FrameVec[prefix.fsi]]};

ValidFrame: PROC[f: PrincOps.FrameHandle]
RETURNS[BOOL] =
INLINE
{RETURN[f # PrincOps.NullFrame AND LOOPHOLE[f, CARDINAL] MOD 4 = 0]};

InvalidProcess: PROC[px: PSB.PsbIndex] RETURNS[BOOL] =
{ Process.ValidateProcess[px ! Process.InvalidProcess => GOTO ng];
RETURN[FALSE];
EXITS ng => RETURN[TRUE]};


PageFromLongPointer: PROC[lp: LONG POINTER]
RETURNS [page: Space.PageNumber] =
INLINE
{OPEN LOOPHOLE[lp, num Environment.Long]; RETURN[highbits*256+lowbits/256]};

Yield: PROC =
INLINE
{ ProcessInternal.DisableInterrupts[]; -- (to cancel subsequent enable)
ProcessOperations.EnableAndRequeue[@PSB.PDA.ready,
@PSB.PDA.ready,
ProcessOperations.ReadPSB[]] };

RTProcessStarted: PUBLIC ENTRY PROC =
{ENABLE UNWIND => NULL;
rtProcessStarted ← TRUE;
FOR psbi: PSB.PsbIndex IN [1..maxCedarPSBI]
DO IF CedarProcessRegistry[psbi] THEN RTProcessPrivate.NewCedarProcessRegistered[psbi];
ENDLOOP};


-- START HERE

-- pre-allocate space for the frameheap snapshot 'cause collection maybe for VMExhausted
frameBodyMasterSpace ← Space.Create[size: frameBodyMasterPages, parent: Space.virtualMemory];

END.