<> <> <> <<>> <> <<>> DIRECTORY Inline USING [ BITSHIFT ], IntervalTimer USING [ Microseconds, Now, NowInPulses, Pulses ], IntervalTimerFace USING [ exists, SetExpirationTime, Wait ], <> <<(This implementation borrows the insertion/removal algorithm from the PQ package)>> Process USING [ Detach, Priority, priorityInterrupt, SetPriority ], ProcessOperations USING [ HandleToIndex, ReadPSB ], RTOS USING [ UnregisterCedarProcess ], Runtime USING [ GlobalFrame ], Space USING [ Create, Handle, LongPointer, Map, virtualMemory, wordsPerPage ], SpecialSpace USING [ MakeResident, MakeCodeResident, MakeGlobalFrameResident ], System USING [ MicrosecondsToPulses, PulsesToMicroseconds ] ; IntervalTimerImpl: CEDAR MONITOR IMPORTS Inline, IntervalTimer, IntervalTimerFace, Process, ProcessOperations, RTOS, Runtime, Space, SpecialSpace, System EXPORTS IntervalTimer = { TooManyWaiters: PUBLIC ERROR = CODE; IntervalItem: TYPE = POINTER TO IntervalRec; IntervalRec: TYPE = RECORD [ time: IntervalTimer.Pulses, wakeup: CONDITION, timedOut: BOOLEAN ]; QIndex: TYPE = NAT; maxQueueLength: QIndex = Space.wordsPerPage - 5; -- max # processes is 1024 these days PSeq: TYPE = LONG POINTER TO PSequ; PSequ: TYPE = RECORD [ queueLength: QIndex, elements: ARRAY [0..maxQueueLength) OF IntervalItem ]; spaceHandle: Space.Handle; intervalQ: PSeq _ NIL; timerIdle: CONDITION; <> <<>> SoftNow: PROC RETURNS [now: IntervalTimer.Microseconds] = { RETURN[IntervalTimer.Now[]]; }; SoftNowInPulses: PROC RETURNS [now: IntervalTimer.Pulses] = { RETURN[IntervalTimer.NowInPulses[]]; }; WaitForExpirationTimeInPulses: PUBLIC ENTRY PROCEDURE[time: IntervalTimer.Pulses] = TRUSTED { item: IntervalRec; IF ~IntervalTimerFace.exists THEN RETURN; item.time _ time; item.timedOut_FALSE; IF ~PQInsert[@item] THEN ERROR TooManyWaiters; <> IntervalTimerFace.SetExpirationTime[PQTop[].time]; NOTIFY timerIdle; <> UNTIL item.timedOut DO WAIT item.wakeup; ENDLOOP }; WaitForExpirationTime: PUBLIC PROCEDURE[time: IntervalTimer.Microseconds] = TRUSTED { nowInPulses: IntervalTimer.Pulses; timeToWait: IntervalTimer.Microseconds; IF ~IntervalTimerFace.exists THEN RETURN; nowInPulses _ IntervalTimer.NowInPulses[]; timeToWait _ time - System.PulsesToMicroseconds[nowInPulses]; IF LOOPHOLE[timeToWait, INT] <= 0 THEN RETURN; -- in the past already WaitForExpirationTimeInPulses[[nowInPulses + System.MicrosecondsToPulses[timeToWait]]]; }; WaitForExpirationInterval: PUBLIC PROCEDURE[microseconds: INT] = TRUSTED { WaitForExpirationTimeInPulses[[IntervalTimer.NowInPulses[] + System.MicrosecondsToPulses[microseconds]]]; }; WaitForExpirationIntervalInPulses: PUBLIC PROCEDURE[pulses: INT] = TRUSTED { WaitForExpirationTimeInPulses[[IntervalTimer.NowInPulses[] + pulses]]; }; IntervalTimerMultiplexor: PROC = TRUSTED { Notifier: ENTRY PROC = TRUSTED INLINE { PQTop[].timedOut _ TRUE; NOTIFY PQTop[].wakeup; PQRemove[]; }; WaitForWork: ENTRY PROC = TRUSTED INLINE { WHILE PQEmpty[] DO WAIT timerIdle; ENDLOOP; IntervalTimerFace.SetExpirationTime[PQTop[].time]; }; SpecialSpace.MakeCodeResident[Runtime.GlobalFrame[IntervalTimerMultiplexor]]; SpecialSpace.MakeGlobalFrameResident[Runtime.GlobalFrame[IntervalTimerMultiplexor]]; RTOS.UnregisterCedarProcess[ProcessOperations.HandleToIndex[ProcessOperations.ReadPSB[]]]; Process.SetPriority[Process.priorityInterrupt]; DO WaitForWork[]; IntervalTimerFace.Wait[]; Notifier[]; ENDLOOP; }; InitializeIntervalTimer: PROC = TRUSTED { spaceHandle _ Space.Create[size: 1, parent: Space.virtualMemory]; Space.Map[spaceHandle]; SpecialSpace.MakeResident[spaceHandle]; intervalQ _ Space.LongPointer[spaceHandle]; intervalQ.queueLength _ 0; Process.Detach[FORK IntervalTimerMultiplexor[]]; }; <> <<>> <> PQTop: PROC RETURNS [IntervalItem] = TRUSTED INLINE { RETURN[intervalQ.elements[1]]; }; PQEmpty: PROC RETURNS [BOOL] = TRUSTED INLINE { RETURN[intervalQ.queueLength = 0]; }; PQInsert: INTERNAL PROC [item: IntervalItem] RETURNS [ok: BOOL _ TRUE] = TRUSTED INLINE { size, son: QIndex _ intervalQ.queueLength + 1; dad: QIndex _ Inline.BITSHIFT[son, -1]; IF size > maxQueueLength THEN RETURN[FALSE]; WHILE dad > 0 AND LOOPHOLE[intervalQ.elements[dad].time - item.time, INT] > 0 DO <> intervalQ.elements[son] _ intervalQ.elements[dad]; son_dad; dad _ Inline.BITSHIFT[son, -1]; ENDLOOP; intervalQ.elements[son] _ item; -- insert the new item and intervalQ.queueLength _ size; -- update the size. }; <> PQRemove: INTERNAL PROC = TRUSTED INLINE { size: QIndex _ intervalQ.queueLength; item: IntervalItem _ NIL; dad, maxDad: QIndex; IF size = 0 THEN ERROR; -- Paranoid: assert that PQRemove is only called when non-empty item _ intervalQ.elements[size]; intervalQ.elements[size] _ NIL; intervalQ.queueLength _ (size _ size - 1); IF size = 0 THEN RETURN; dad _ 1; maxDad _ Inline.BITSHIFT[size, -1]; WHILE dad <= maxDad DO son: QIndex _ dad + dad; IF son < size THEN IF LOOPHOLE[intervalQ.elements[son].time - intervalQ.elements[son + 1].time, INT] > 0 THEN son _ son + 1; IF LOOPHOLE[intervalQ.elements[son].time - item.time, INT] > 0 THEN EXIT; intervalQ.elements[dad] _ intervalQ.elements[son]; dad _ son; ENDLOOP; intervalQ.elements[dad] _ item; }; InitializeIntervalTimer[]; }.