CommTimerImpl.mesa
Copyright Ó 1988, 1991 by Xerox Corporation. All rights reserved.
Demers, October 11, 1990 8:36 am PDT
Willie-s, March 24, 1992 3:33 pm PST
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
~ {
Copied Types
EventProc: TYPE ~ CommTimer.EventProc;
Timer: TYPE ~ REF TimerObject;
TimerObject: PUBLIC TYPE ~ CommTimerPrivate.TimerObject;
Event: TYPE ~ CommTimer.Event;
EventObject: PUBLIC TYPE ~ CommTimerPrivate.EventObject;
Parameters
msecPerTick: INT ¬ Process.MsecToTicks[1];
maxSizeOfWheel: CARDINAL ¬ 256;
Glue
MsecSinceBoot: PROC RETURNS [msec: INT] ~ {
msec ¬ LOOPHOLE[Process.TicksToMsec[CommTimerPrivate.TicksSinceBoot[]]];
};
Error
Error: PUBLIC ERROR [codes: LIST OF ATOM, msg: Rope.ROPE] ~ CODE;
Event Freelist
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;
};
Daemon
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 => {
We've fallen way behind, so lose some time ...
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 => {
We've fallen incredibly far behind and wrapped, so lose some time ...
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;
};
Operations
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] ];
};
}.