XlTimeEventsImpl.mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, September 26, 1991 4:33:22 pm PDT
Christian Jacobi, May 28, 1993 12:54 pm PDT
DIRECTORY
Process,
Xl,
XlTimeEvents;
XlTimeEventsImpl: CEDAR MONITOR
IMPORTS Process, Xl
EXPORTS XlTimeEvents ~
BEGIN OPEN XlTimeEvents;
actualTicks: PUBLIC CARD32 ¬ TicksSinceBoot[]; --May be late; see below.
Actual PCR might temporarily make its clock run very fast to catch up missed interupts. We will simply ignore advancement larger then a certain limit. This limit may be too high and therefore not prevent actualTicks from temporarily running fast. However this limits the amount by which actualTicks may run fast to a period smaller then fakeLimit.
mSecPerFastTick: PUBLIC CARD32 ¬ MAX[TicksToMsec[700]/1000, 1]; --makes clock slow
tehQueue: TEHandle ¬ NIL;
fakeLimit: CARD32 ¬ Process.SecondsToTicks[3]; --limit the time period when timeout events are sent to limit actions of a quiescent system.
fakeEvent: REF Xl.EventRep.local ~ NewFakeEvent[]; --used in case client checks event type
NewFakeEvent: PROC [] RETURNS [e: REF Xl.EventRep.local] = {
e ¬ NEW[Xl.EventRep.local];
e.who ¬ $TimeEnforcer
};
TicksSinceBoot: PROC [] RETURNS [CARD32] = TRUSTED MACHINE CODE {
"XR←TicksSinceBoot"
};
TicksToMsec: PROC [t: CARD32] RETURNS [CARD32] = TRUSTED MACHINE CODE {
"XR←TicksToMsec"
};
NilProc: Xl.EventProcType = {};
Create: PUBLIC PROC [reportTQ: Xl.TQ, reportProc: Xl.EventProcType, reportData: REF ¬ NIL] RETURNS [teh: TEHandle] = {
IF reportProc=NIL THEN reportProc ¬ NilProc;
IF reportTQ=NIL THEN ERROR;
teh ¬ NEW[TEHandleRec ¬ [tq: reportTQ, proc: reportProc, data: reportData]];
};
RealActivate: PUBLIC ENTRY PROC [teh: TEHandle] = {
teh.active ¬ TRUE;
IF ~teh.enqueued THEN {
teh.enqueued ¬ TRUE;
teh.next ¬ tehQueue;
tehQueue ¬ teh
};
};
Cleanup: PUBLIC ENTRY PROC = {
--Removes inactive handles from list inspected at each wakeup
WHILE tehQueue#NIL AND ~tehQueue.active DO
teh: TEHandle ¬ tehQueue;
teh.enqueued ¬ FALSE;
tehQueue ¬ teh.next;
teh.next ¬ NIL;
ENDLOOP;
BEGIN
lag: TEHandle ¬ tehQueue;
IF lag#NIL THEN {
WHILE lag.next#NIL DO
IF lag.next.active
THEN lag ¬ lag.next
ELSE {
teh: TEHandle ¬ lag.next;
teh.enqueued ¬ FALSE;
lag.next ¬ teh.next;
teh.next ¬ NIL;
};
ENDLOOP
};
END;
};
TimeEnforceProcess: PROC [] = {
count: INT ¬ 0;
lastTicks: CARD32 ¬ actualTicks;
thisTicks: CARD32 ¬ actualTicks;
deltaTicks: Process.Ticks ~ MAX[Process.MsecToTicks[30], 1]; --pause length, set small
maxDeltaTicks: CARD32 ~ MAX[Process.MsecToTicks[30], 1]; --prevent propagating fast clock; >=deltaTicks
Process.SetPriority[Process.priorityClient3];
DO --forever
Process.Pause[deltaTicks];
IF count<100
THEN count ¬ count+1
ELSE {count ¬ 0; Cleanup[]};
thisTicks ¬ TicksSinceBoot[];
actualTicks ¬ actualTicks + MIN[maxDeltaTicks, thisTicks-lastTicks];
lastTicks ¬ thisTicks;
FOR teh: TEHandle ¬ tehQueue, teh.next WHILE teh#NIL DO
IF teh.active THEN Visit[teh];
ENDLOOP;
ENDLOOP;
};
Visit: PROC [teh: TEHandle] = {
IF teh.baseValid
THEN {
--only visit if waiting at least a full delta to guarantee slowness of artificial clock
Xl.Enqueue[teh.tq, teh.proc, teh.data, fakeEvent];
IF (actualTicks-teh.lastWound <<MOD 2**32>> ) > fakeLimit THEN {
--Use clients thread because of monitoring convention
Xl.Enqueue[teh.tq, DeActivator, teh, fakeEvent];
};
}
ELSE {
teh.baseValid ¬ TRUE;
teh.lastActualTicks ¬ actualTicks
};
};
DeActivator: Xl.EventProcType = {
--De-activate on the clients thread so teh.active is monitored against Activate
teh: TEHandle ~ NARROW[clientData];
IF (actualTicks-teh.lastWound <<MOD 2**32>> ) > fakeLimit THEN {
teh.active ¬ FALSE
};
};
TRUSTED {Process.Detach[FORK TimeEnforceProcess[]]};
END.