-- RTPageFaultImpl.Mesa
-- ******* NOTE COMPILE THIS /-c *********
-- last edited 16-Mar-82 11:09:05 by Paul Rovner
DIRECTORY
Process USING [SetPriority, priorityInterrupt, Detach, Pause, MsecToTicks, priorityNormal],
ProcessInternal USING[EnableInterrupts, DisableInterrupts],
ProcessOperations USING[HandleToIndex, ReadPSB, LongNotify, LongWait, LongReEnter],
PSB USING[PsbIndex, PsbNull, PDA, qPageFault, Condition],
RTOS USING[UnregisterCedarProcess],
RTProcess USING[], -- EXPORTS only
RTProcessPrivate USING[], -- EXPORTS only
Runtime USING[GlobalFrame],
SpecialSpace USING[MakeCodeResident, MakeCodeSwappable, MakeGlobalFrameResident--, MakeGlobalFrameSwappable--];
RTPageFaultImpl: MONITOR
IMPORTS Process, ProcessInternal, ProcessOperations, RTOS, Runtime, SpecialSpace
EXPORTS RTProcess, RTProcessPrivate
= BEGIN
--constants, variables, TYPEs
Int: TYPE = LONG INTEGER;
recorderLock: MONITORLOCK;
pPageFaultCondition: LONG POINTER TO PSB.Condition = @PSB.PDA.fault[PSB.qPageFault].condition;
pPageFaultCONDITION: LONG POINTER TO CONDITION = LOOPHOLE[pPageFaultCondition];
totalPageFaults: Int ← 0;
MaxPSBIVecIndex: NAT = 40;
NextPSBIVecIndex: NAT ← 0;
PSBIVec: ARRAY [0..MaxPSBIVecIndex] OF PSBIEntry;
PSBIEntry: TYPE = RECORD[psbi: PSB.PsbIndex ← PSB.PsbNull, count: Int ← 0];
faultWatcherRunning: BOOLEAN ← FALSE;
faultWatcherStartCount: NAT ← 0;
-- PUBLIC procs
StartWatchingFaults: PUBLIC ENTRY PROC =
{ENABLE UNWIND => NULL;
IF (faultWatcherStartCount ← faultWatcherStartCount + 1) = 1
THEN {faultWatcherRunning ← TRUE;
Process.Detach[FORK FaultWatcher]}};
StopWatchingFaults: PUBLIC PROC =
{ firstCall: BOOLEAN ← TRUE;
UNTIL DoStopWatchingFaults[firstCall]
DO firstCall ← FALSE;
Process.Pause[Process.MsecToTicks[120]]
ENDLOOP};
DoStopWatchingFaults: ENTRY PROC[firstCall: BOOLEAN]
RETURNS[success: BOOLEAN] =
{ ENABLE UNWIND => NULL;
IF firstCall
THEN {IF faultWatcherStartCount = 0 THEN RETURN[TRUE];
faultWatcherStartCount ← faultWatcherStartCount - 1};
RETURN[faultWatcherStartCount # 0 OR NOT faultWatcherRunning]};
-- NOTE not an ENTRY, to avoid deadlock
InvalidateFaultingCedarProcess: PUBLIC PROC[psbi: PSB.PsbIndex] =
{ IF NOT faultWatcherRunning THEN RETURN;
FOR i: NAT IN [0..NextPSBIVecIndex)
DO IF PSBIVec[i].psbi = psbi THEN {PSBIVec[i] ← []; EXIT}
ENDLOOP};
GetTotalPageFaults: PUBLIC PROC RETURNS[LONG INTEGER] = {RETURN[totalPageFaults]};
ClearFaultHistory: PUBLIC ENTRY PROC =
{ ENABLE UNWIND => NULL;
totalPageFaults ← NextPSBIVecIndex ← 0};
GetPSBIPageFaults: PUBLIC ENTRY PROC[psbi: PSB.PsbIndex] RETURNS[Int] =
{ ENABLE UNWIND => NULL;
FOR i: NAT IN [0..NextPSBIVecIndex)
DO IF PSBIVec[i].psbi = psbi
THEN RETURN[PSBIVec[i].count];
ENDLOOP;
RETURN[0]};
EnumerateFaultingProcesses:
PUBLIC PROC[p: PROC[psbi: PSB.PsbIndex,
pageFaultCount, numberEnumerated: LONG INTEGER] RETURNS[stop: BOOLEAN]]
RETURNS[stopped: BOOLEAN] =
{npsbi: NAT = NextPSBIVecIndex;
FOR i: NAT IN [0..npsbi)
DO psbi: PSB.PsbIndex = PSBIVec[i].psbi;
count: Int = PSBIVec[i].count;
IF count # 0 AND psbi # PSB.PsbNull THEN IF p[psbi, count, npsbi] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE]};
-- high priority stuff for monitoring page fault activity
BumpPSBIFaultCounter: PROC[psbi: PSB.PsbIndex] =
INLINE
{empty: NAT ← NextPSBIVecIndex;
totalPageFaults ← totalPageFaults + 1;
FOR i: NAT IN [0..NextPSBIVecIndex)
DO IF PSBIVec[i].psbi = psbi
THEN {PSBIVec[i].count ← PSBIVec[i].count + 1; RETURN}
ELSE IF PSBIVec[i].psbi = PSB.PsbNull
THEN empty ← i;
ENDLOOP;
IF empty > MaxPSBIVecIndex THEN RETURN; -- forget it.
PSBIVec[empty] ← [psbi: psbi, count: 1];
IF empty = NextPSBIVecIndex THEN NextPSBIVecIndex ← NextPSBIVecIndex + 1};
GetCurrent: PROC RETURNS[PSB.PsbIndex] =
INLINE
{RETURN[ProcessOperations.HandleToIndex[ProcessOperations.ReadPSB[]]]};
LongNakedNotify: PROC [pCondition: LONG POINTER TO CONDITION] =
INLINE
{-- used to notify a condition from outside the relevant monitor.
pCond: LONG POINTER TO PSB.Condition = LOOPHOLE[pCondition];
ProcessInternal.DisableInterrupts[];
IF pCond↑.tail=PSB.PsbNull
THEN
{ pCond↑.wakeup ← TRUE;
ProcessInternal.EnableInterrupts[] }
ELSE
{ ProcessInternal.EnableInterrupts[];
ProcessOperations.LongNotify[pCondition] } };
WaitForFault: PROC =
INLINE
{ProcessOperations.LongWait
[@recorderLock, pPageFaultCONDITION, --timeout:-- 1];
UNTIL ProcessOperations.LongReEnter
[@recorderLock, pPageFaultCONDITION] DO
NULL ENDLOOP;
-- either a new page fault came along or we timed out..
IF PSB.PDA.fault[PSB.qPageFault].queue.tail = PSB.PsbNull
THEN RETURN; -- just timed out, so forget it
BumpPSBIFaultCounter[PSB.PDA.fault[PSB.qPageFault].queue.tail];
-- conditionally wake up the Pilot fault handler:
ProcessInternal.DisableInterrupts[];
IF pPageFaultCondition↑.tail # PSB.PsbNull
THEN LongNakedNotify[pPageFaultCONDITION];
ProcessInternal.EnableInterrupts[];
};
-- this guy runs as a detached process.
FaultWatcher: PROC =
{ -- make the fault watcher resident
SpecialSpace.MakeGlobalFrameResident[RTPageFaultImpl];
SpecialSpace.MakeCodeResident[Runtime.GlobalFrame[FaultWatcher]];
-- unregister self
RTOS.UnregisterCedarProcess[GetCurrent[]];
-- raise priority
Process.SetPriority[Process.priorityInterrupt];
DO IF faultWatcherStartCount = 0 THEN EXIT ELSE WaitForFault[] ENDLOOP;
Process.SetPriority[Process.priorityNormal];
SpecialSpace.MakeCodeSwappable[Runtime.GlobalFrame[FaultWatcher]];
-- SpecialSpace.MakeGlobalFrameSwappable[RTPageFaultImpl];
faultWatcherRunning ← FALSE};
END.