ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/timers.b/
implement Timers;
include "sys.m";
sys: Sys;
include "timers.m";
timerin: chan of ref Timer;
init(minms: int): int
{
sys = load Sys Sys->PATH;
timerin = chan[20] of ref Timer;
if(minms <= 0)
minms = 1;
pid := chan of int;
spawn timeproc(timerin, minms, pid);
return <-pid;
}
shutdown()
{
if(timerin != nil)
timerin <-= nil;
}
Timer.start(dt: int): ref Timer
{
t := ref Timer(dt, chan[1] of int);
timerin <-= t;
return t;
}
Timer.stop(t: self ref Timer)
{
# this is safe, because only Timer.stop sets t.timeout and timeproc only fetches it
t.timeout = nil;
}
timeproc(req: chan of ref Timer, msec: int, pid: chan of int)
{
pending: list of ref Timer;
pid <-= sys->pctl(Sys->NEWFD|Sys->NEWNS|Sys->NEWENV, nil); # same pgrp
old := sys->millisec();
Work:
for(;;){
if(pending == nil){
if((t := <-req) == nil)
break Work;
pending = t :: pending;
old = sys->millisec();
}else{
# check quickly for new requests
Check:
for(;;) alt{
t := <-req =>
if(t == nil)
break Work;
pending = t :: pending;
* =>
break Check;
}
}
sys->sleep(msec);
new := sys->millisec();
dt := new-old;
old = new;
if(dt < 0)
continue; # millisec counter wrapped
ticked := 0;
for(l := pending; l != nil; l = tl l)
if(((hd l).dt -= dt) <= 0)
ticked = 1;
if(ticked){
l = pending;
pending = nil;
for(; l != nil; l = tl l){
t := hd l;
if(t.dt > 0 || !notify(t))
pending = t :: pending;
}
}
}
# shut down: attempt to clear pending requests
for(; pending != nil; pending = tl pending)
notify(hd pending);
}
notify(t: ref Timer): int
{
# copy to c to avoid race with Timer.stop
if((c := t.timeout) == nil)
return 1; # cancelled; consider it done
alt{
c <-= 1 => return 1;
* => return 0;
}
}