<<>> <> <> <> <> <<>> DIRECTORY Process, Xl, XlTimeEvents; XlTimeEventsImpl: CEDAR MONITOR IMPORTS Process, Xl EXPORTS XlTimeEvents ~ BEGIN OPEN XlTimeEvents; actualTicks: PUBLIC CARD32 ¬ TicksSinceBoot[]; --May be late; see below. <> 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 <> ) > 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 <> ) > fakeLimit THEN { teh.active ¬ FALSE }; }; TRUSTED {Process.Detach[FORK TimeEnforceProcess[]]}; END.