<> <> <> <> DIRECTORY CommTimer USING [Event, EventProc, IsNullEvent, nullEvent], CommTimerPrivate USING [EventObject, TicksSinceBoot, TimerObject], Process USING [Abort, Detach, EnableAborts, MsecToTicks, priorityClient3, SetPriority, SetTimeout, Ticks, TicksToMsec], Rope USING [ROPE] ; CommTimerImpl: CEDAR MONITOR LOCKS lock USING lock: Timer IMPORTS Process, CommTimer, CommTimerPrivate EXPORTS CommTimer ~ { <> EventProc: TYPE ~ CommTimer.EventProc; Timer: TYPE ~ REF TimerObject; TimerObject: PUBLIC TYPE ~ CommTimerPrivate.TimerObject; Event: TYPE ~ CommTimer.Event; EventObject: PUBLIC TYPE ~ CommTimerPrivate.EventObject; <> msecPerTick: INT ¬ Process.MsecToTicks[1]; maxSizeOfWheel: CARDINAL ¬ 256; <> MsecSinceBoot: PROC RETURNS [msec: INT] ~ { msec ¬ LOOPHOLE[Process.TicksToMsec[CommTimerPrivate.TicksSinceBoot[]]]; }; <> Error: PUBLIC ERROR [codes: LIST OF ATOM, msg: Rope.ROPE] ~ CODE; <> eventLock: Timer ¬ NEW[TimerObject[0]]; freeList: REF EventObject ¬ NIL; AllocEvent: ENTRY PROC [lock: Timer ¬ eventLock] RETURNS [e: Event] ~ { IF freeList = NIL THEN { e.ref ¬ NEW[EventObject]; } ELSE { e.ref ¬ freeList; freeList ¬ e.ref.next; e.ref.next ¬ NIL; }; e.gen ¬ e.ref.gen; }; FreeEvent: ENTRY PROC [lock: Timer ¬ eventLock, re: REF EventObject] ~ { re.gen ¬ re.gen.SUCC; re.next ¬ freeList; freeList ¬ re; }; <> EntryWaitForEvent: ENTRY PROC [lock: Timer] RETURNS [list: REF EventObject] ~ { ENABLE UNWIND => NULL; t: Timer ~ lock; nowMsec, rangeMsec, delta: INT; rangeMsec ¬ t.wheelSize * t.grainSizeMsec; DO nowMsec ¬ MsecSinceBoot[]; delta ¬ nowMsec - t.nextMsec; SELECT delta FROM > 2 * rangeMsec => { <> t.nextMsec ¬ nowMsec - rangeMsec; EXIT }; >= 0 => { t.nextMsec ¬ t.nextMsec + t.grainSizeMsec; EXIT }; >= (-t.grainSizeMsec) => { ticks: Process.Ticks ¬ Process.MsecToTicks[-delta]; TRUSTED { Process.SetTimeout[@t.wakeup, ticks] }; WAIT t.wakeup; LOOP }; ENDCASE => { <> t.nextMsec ¬ nowMsec - rangeMsec; EXIT }; ENDLOOP; { lDue, lNotDue, e, next: REF EventObject; FOR e ¬ t.wheel[t.wheelIndex], next WHILE e # NIL DO next ¬ e.next; IF (e.whenMsec - t.nextMsec) < 0 THEN { e.next ¬ lDue; lDue ¬ e } ELSE { e.next ¬ lNotDue; lNotDue ¬ e }; ENDLOOP; t.wheel[t.wheelIndex] ¬ lNotDue; list ¬ lDue; }; t.wheelIndex ¬ t.wheelIndex.SUCC MOD t.wheelSize; }; DoEvents: PROC [list: REF EventObject, doCalls: BOOL ¬ TRUE] ~ { WHILE list # NIL DO e: REF EventObject ¬ list; clientData: REF ¬ e.clientData; proc: EventProc ¬ e.proc; list ¬ list.next; IF (proc # NIL) AND doCalls THEN proc[clientData]; FreeEvent[eventLock, e]; ENDLOOP; }; Daemon: PROC [t: Timer] ~ { ENABLE ABORTED => CONTINUE; Process.SetPriority[Process.priorityClient3]; DO list: REF EventObject ¬ EntryWaitForEvent[t]; DoEvents[list]; ENDLOOP; }; <> CreateTimer: PUBLIC PROC [grainSizeMsec: INT, expectedWaitMsec: INT] RETURNS [t: Timer] ~ { sizeOfWheel: CARDINAL; IF grainSizeMsec < 0 THEN ERROR Error[LIST[$badGrainSize], "negative grain size"]; grainSizeMsec ¬ MAX[msecPerTick, grainSizeMsec]; expectedWaitMsec ¬ MAX[expectedWaitMsec, grainSizeMsec]; sizeOfWheel ¬ expectedWaitMsec / grainSizeMsec; IF sizeOfWheel < maxSizeOfWheel THEN sizeOfWheel ¬ sizeOfWheel + sizeOfWheel/2; sizeOfWheel ¬ MIN[sizeOfWheel, maxSizeOfWheel]; t ¬ NEW[TimerObject[sizeOfWheel]]; t.grainSizeMsec ¬ grainSizeMsec; t.nextMsec ¬ MsecSinceBoot[]; TRUSTED { Process.EnableAborts[@t.wakeup] }; t.daemon ¬ FORK Daemon[t]; }; EntryDestroyTimer: ENTRY PROC [lock: Timer] ~ { lock.destroyed ¬ TRUE; }; DestroyTimer: PUBLIC PROC [t: Timer, doEvents: BOOL] ~ { p: PROCESS; EntryDestroyTimer[t]; IF (p ¬ t.daemon) # NIL THEN TRUSTED { Process.Abort[p]; Process.Detach[p]; }; FOR i: CARDINAL IN [0..t.wheelSize) DO list: REF EventObject; list ¬ t.wheel[i]; t.wheel[i] ¬ NIL; DoEvents[list, doEvents]; ENDLOOP; }; GrainSizeMsec: PUBLIC PROC [t: Timer] RETURNS [grainSizeMsec: INT] ~ { RETURN[t.grainSizeMsec] }; EntryScheduleEvent: PUBLIC ENTRY PROC [lock: Timer, waitMsec: INT, e: Event] RETURNS [Event] ~ { index: CARDINAL; IF lock.destroyed THEN RETURN [CommTimer.nullEvent]; e.ref.whenMsec ¬ waitMsec + lock.nextMsec; index ¬ ((waitMsec / lock.grainSizeMsec) + lock.wheelIndex) MOD lock.wheelSize; e.ref.next ¬ lock.wheel[index]; lock.wheel[index] ¬ e.ref; RETURN [e]; }; ScheduleEvent: PUBLIC PROC [t: Timer, waitMsec: INT, proc: EventProc, clientData: REF] RETURNS [Event] ~ { e: Event ¬ AllocEvent[eventLock]; e.ref.proc ¬ proc; e.ref.clientData ¬ clientData; IF waitMsec > 0 THEN { e ¬ EntryScheduleEvent[t, waitMsec, e]; IF CommTimer.IsNullEvent[e] THEN ERROR Error[LIST[$staleTimer], "timer destroyed"]; } ELSE { DoEvents[e.ref]; }; RETURN[ e ]; }; EntryCancelEvent: ENTRY PROC [lock: Timer ¬ eventLock, e: Event] ~ { IF e.gen # e.ref.gen THEN RETURN; e.ref.proc ¬ NIL; e.ref.clientData ¬ NIL; }; CancelEvent: PUBLIC PROC [t: Timer, e: Event] ~ { EntryCancelEvent[eventLock, e]; }; EntryMsecUntilOccurs: ENTRY PROC [lock: Timer ¬ eventLock, t: Timer, e: Event] RETURNS [INT] ~ { RETURN[ IF e.ref.gen = e.gen THEN e.ref.whenMsec - t.nextMsec ELSE 0 ]; }; MsecUntilOccurs: PUBLIC PROC [t: Timer, e: Event] RETURNS [INT] ~ { RETURN[ EntryMsecUntilOccurs[eventLock, t, e] ]; }; }.