ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/lego/timers.b/
# Chris Locke. June 2000
# TODO: for auto-repeat timers don't set up a new sender
# if there is already a pending sender for that timer.
implement Timers;
include "sys.m";
include "timers.m";
RealTimer : adt {
t : ref Timer;
nticks : int;
rep : int;
nexttick: big;
tick : chan of int;
sender : int;
};
Sender : adt {
tid : int;
idle : int; # set by sender() when done, reset by main when about to assign work
ctl : chan of chan of int;
};
sys : Sys;
acquire : chan of int;
timers := array [4] of ref RealTimer;
senders := array [4] of ref Sender;
curtick := big 0;
tickres : int;
init(res : int)
{
sys = load Sys Sys->PATH;
acquire = chan of int;
tickres = res;
spawn main();
}
new(ms, rep : int) : ref Timer
{
acquire <- = 1;
t := do_new(ms, rep);
<- acquire;
return t;
}
Timer.destroy(t : self ref Timer)
{
acquire <- = 1;
do_destroy(t);
<- acquire;
}
Timer.reset(t : self ref Timer)
{
acquire <- = 1;
do_reset(t);
<- acquire;
}
Timer.cancel(t : self ref Timer)
{
acquire <- = 1;
do_cancel(t);
<- acquire;
}
# only call under lock
#
realtimer(t : ref Timer) : ref RealTimer
{
if (t.id < 0 || t.id >= len timers)
return nil;
if (timers[t.id] == nil)
return nil;
if (timers[t.id].t != t)
return nil;
return timers[t.id];
}
# called under lock
#
do_destroy(t : ref Timer)
{
rt := realtimer(t);
if (rt == nil)
return;
clearsender(rt, t.id);
timers[t.id] = nil;
}
# called under lock
#
do_reset(t : ref Timer)
{
rt := realtimer(t);
if (rt == nil)
return;
clearsender(rt, t.id);
rt.nexttick = curtick + big (rt.nticks);
startclk = 1;
}
# called under lock
#
do_cancel(t : ref Timer)
{
rt := realtimer(t);
if (rt == nil)
return;
clearsender(rt, t.id);
rt.nexttick = big 0;
}
# only call under lock
#
clearsender(rt : ref RealTimer, tid : int)
{
# check to see if there is a sender trying to deliver tick
if (rt.sender != -1) {
sender := senders[rt.sender];
rt.sender = -1;
if (sender.tid == tid && !sender.idle) {
# receive the tick to clear the busy state
alt {
<- rt.tick =>
;
* =>
;
}
}
}
}
# called under lock
do_new(ms, rep : int) : ref Timer
{
# find free slot
for (i := 0; i < len timers; i++)
if (timers[i] == nil)
break;
if (i == len timers) {
# grow the array
newtimers := array [len timers * 2] of ref RealTimer;
newtimers[0:] = timers;
timers = newtimers;
}
tick := chan of int;
t := ref Timer(i, tick);
nticks := ms / tickres;
if (nticks == 0)
nticks = 1;
rt := ref RealTimer(t, nticks, rep, big 0, tick, -1);
timers[i] = rt;
return t;
}
startclk : int;
stopclk : int;
main()
{
clktick := chan of int;
clkctl := chan of int;
clkstopped := 1;
spawn ticker(tickres, clkctl, clktick);
for (;;) alt {
<- acquire =>
# Locking
acquire <- = 1;
if (clkstopped && startclk) {
clkstopped = 0;
startclk = 0;
clkctl <- = 1;
}
t := <- clktick =>
if (t == 0) {
stopclk = 0;
if (startclk) {
startclk = 0;
clkctl <- = 1;
} else {
clkstopped = 1;
continue;
}
}
curtick++;
npend := 0;
for (i := 0; i < len timers; i++) {
rt := timers[i];
if (rt == nil)
continue;
if (rt.nexttick == big 0)
continue;
if (rt.nexttick > curtick) {
npend++;
continue;
}
# Timeout - arrange to send the tick
if (rt.rep) {
rt.nexttick = curtick + big rt.nticks;
npend++;
} else
rt.nexttick = big 0;
si := getsender();
s := senders[si];
s.tid = i;
s.idle = 0;
rt.sender = si;
s.ctl <- = rt.tick;
}
if (!npend)
stopclk = 1;
}
}
getsender() : int
{
for (i := 0; i < len senders; i++) {
s := senders[i];
if (s == nil || s.idle == 1)
break;
}
if (i == len senders) {
newsenders := array [len senders * 2] of ref Sender;
newsenders[0:] = senders;
senders = newsenders;
}
if (senders[i] == nil) {
s := ref Sender (-1, 1, chan of chan of int);
spawn sender(s);
senders[i] = s;
}
return i;
}
sender(me : ref Sender)
{
for (;;) {
tickch := <- me.ctl;
tickch <- = 1;
me.idle = 1;
}
}
ticker(ms : int, start, tick : chan of int)
{
for (;;) {
<- start;
while (!stopclk) {
sys->sleep(ms);
tick <- = 1;
}
tick <- = 0;
}
}