xref: /inferno-os/appl/lib/timers.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Timers;
2
3include "sys.m";
4	sys:	Sys;
5
6include "timers.m";
7
8timerin: chan of ref Timer;
9
10init(minms: int): int
11{
12	sys = load Sys Sys->PATH;
13	timerin = chan[20] of ref Timer;
14	if(minms <= 0)
15		minms = 1;
16	pid := chan of int;
17	spawn timeproc(timerin, minms, pid);
18	return <-pid;
19}
20
21shutdown()
22{
23	if(timerin != nil)
24		timerin <-= nil;
25}
26
27Timer.start(dt: int): ref Timer
28{
29	t := ref Timer(dt, chan[1] of int);
30	timerin <-= t;
31	return t;
32}
33
34Timer.stop(t: self ref Timer)
35{
36	# this is safe, because only Timer.stop sets t.timeout and timeproc only fetches it
37	t.timeout = nil;
38}
39
40timeproc(req: chan of ref Timer, msec: int, pid: chan of int)
41{
42	pending: list of ref Timer;
43
44	pid <-= sys->pctl(Sys->NEWFD|Sys->NEWNS|Sys->NEWENV, nil);	# same pgrp
45	old := sys->millisec();
46Work:
47	for(;;){
48		if(pending == nil){
49			if((t := <-req) == nil)
50				break Work;
51			pending = t :: pending;
52			old = sys->millisec();
53		}else{
54			# check quickly for new requests
55		Check:
56			for(;;) alt{
57			t := <-req =>
58				if(t == nil)
59					break Work;
60				pending = t :: pending;
61			* =>
62				break Check;
63			}
64		}
65		sys->sleep(msec);
66		new := sys->millisec();
67		dt := new-old;
68		old = new;
69		if(dt < 0)
70			continue;	# millisec counter wrapped
71		ticked := 0;
72		for(l := pending; l != nil; l = tl l)
73			if(((hd l).dt -= dt) <= 0)
74				ticked = 1;
75		if(ticked){
76			l = pending;
77			pending = nil;
78			for(; l != nil; l = tl l){
79				t := hd l;
80				if(t.dt > 0 || !notify(t))
81					pending = t :: pending;
82			}
83		}
84	}
85	# shut down: attempt to clear pending requests
86	for(; pending != nil; pending = tl pending)
87		notify(hd pending);
88}
89
90notify(t: ref Timer): int
91{
92	# copy to c to avoid race with Timer.stop
93	if((c := t.timeout) == nil)
94		return 1;	# cancelled; consider it done
95	alt{
96	c <-= 1 => return 1;
97	* => return 0;
98	}
99}
100