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