summaryrefslogtreecommitdiff
path: root/appl/cmd/lego/timers.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/lego/timers.b')
-rw-r--r--appl/cmd/lego/timers.b263
1 files changed, 263 insertions, 0 deletions
diff --git a/appl/cmd/lego/timers.b b/appl/cmd/lego/timers.b
new file mode 100644
index 00000000..67e08dec
--- /dev/null
+++ b/appl/cmd/lego/timers.b
@@ -0,0 +1,263 @@
+# 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;
+ }
+}