time.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
time.c (6541B)
---
1 /*
2 * Plan 9 VX timers.
3 *
4 * In Plan 9 VX, "ticks" are milliseconds,
5 * and "fast ticks" are nanoseconds.
6 * This makes the conversions trivial.
7 */
8
9 #include "u.h"
10 #include <pthread.h>
11 #include <signal.h>
12 #include "lib.h"
13 #include "mem.h"
14 #include "dat.h"
15 #include "fns.h"
16 #include "error.h"
17 #include "ureg.h"
18
19 #define nsec() fastticks(nil)
20 #define ns2fastticks(x) (x)
21
22 struct Timers
23 {
24 Lock lk;
25 Timer *head;
26 };
27
28 static vlong start;
29 static Timers timers;
30 static void kicktimerproc(void);
31
32 static vlong
33 tadd(Timers *tt, Timer *nt)
34 {
35 Timer *t, **last;
36
37 /* Called with tt locked */
38 assert(nt->tt == nil);
39 switch(nt->tmode){
40 default:
41 panic("timer");
42 break;
43 case Trelative:
44 if(nt->tns <= 0)
45 nt->tns = 1;
46 nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
47 break;
48 case Tperiodic:
49 assert(nt->tns >= 100000); /* At least 100 µs period */
50 if(nt->twhen == 0){
51 /* look for another timer at same frequency for combining */
52 for(t = tt->head; t; t = t->tnext){
53 if(t->tmode == Tperiodic && t->tns == nt->tns)
54 break;
55 }
56 if (t)
57 nt->twhen = t->twhen;
58 else
59 nt->twhen = fastticks(nil);
60 }
61 nt->twhen += ns2fastticks(nt->tns);
62 break;
63 }
64
65 for(last = &tt->head; (t = *last); last = &t->tnext){
66 if(t->twhen > nt->twhen)
67 break;
68 }
69 nt->tnext = *last;
70 *last = nt;
71 nt->tt = tt;
72 if(last == &tt->head)
73 return nt->twhen;
74 return 0;
75 }
76
77 static uvlong
78 tdel(Timer *dt)
79 {
80 Timer *t, **last;
81 Timers *tt;
82
83 tt = dt->tt;
84 if (tt == nil)
85 return 0;
86 for(last = &tt->head; (t = *last); last = &t->tnext){
87 if(t == dt){
88 assert(dt->tt);
89 dt->tt = nil;
90 *last = t->tnext;
91 break;
92 }
93 }
94 if(last == &tt->head && tt->head)
95 return tt->head->twhen;
96 return 0;
97 }
98
99 /* add or modify a timer */
100 void
101 timeradd(Timer *nt)
102 {
103 Timers *tt;
104 vlong when;
105
106 /* Must lock Timer struct before Timers struct */
107 ilock(&nt->lk);
108 if((tt = nt->tt)){
109 ilock(&tt->lk);
110 tdel(nt);
111 iunlock(&tt->lk);
112 }
113 tt = &timers;
114 ilock(&tt->lk);
115 when = tadd(tt, nt);
116 if(when)
117 kicktimerproc();
118 iunlock(&tt->lk);
119 iunlock(&nt->lk);
120 }
121
122 void
123 timerdel(Timer *dt)
124 {
125 Timers *tt;
126 uvlong when;
127
128 ilock(&dt->lk);
129 if((tt = dt->tt)){
130 ilock(&tt->lk);
131 when = tdel(dt);
132 if(when && tt == &timers)
133 kicktimerproc();
134 iunlock(&tt->lk);
135 }
136 iunlock(&dt->lk);
137 }
138
139 /*
140 * The timer proc sleeps until the next timer is going
141 * to go off, and then runs any timers that need running.
142 * If a new timer is inserted while it is asleep, the proc
143 * that adds the new timer will send the timer proc a SIGURG
144 * to wake it up early. SIGURG is blocked in all procs by default,
145 * so the timer is guaranteed to get the signal.
146 */
147 static pthread_t timer_pid;
148 void
149 timerkproc(void *v)
150 {
151 sigset_t sigs;
152 Timers *tt;
153 Timer *t;
154 uvlong when, now;
155 struct timespec ts;
156 Ureg u;
157 int signo;
158
159 memset(&u, 0, sizeof u);
160 timer_pid = pthread_self();
161
162 tt = &timers;
163 ilock(&tt->lk);
164 for(;;){
165 if((t = tt->head) == nil){
166 iunlock(&tt->lk);
167 sigemptyset(&sigs);
168 sigaddset(&sigs, SIGURG);
169 sigwait(&sigs, &signo);
170 ilock(&tt->lk);
171 continue;
172 }
173 /*
174 * No need to ilock t here: any manipulation of t
175 * requires tdel(t) and this must be done with a
176 * lock to tt held. We have tt, so the tdel will
177 * wait until we're done
178 */
179 now = fastticks(nil);
180 when = t->twhen;
181 if(when > now){
182 iunlock(&tt->lk);
183 when -= now;
184 ts.tv_sec = when/1000000000;
185 ts.tv_nsec = when%1000000000;
186 pthread_sigmask(SIG_SETMASK, nil, &sigs);
187 sigdelset(&sigs, SIGURG);
188 pselect(0, nil, nil, nil, &ts, &sigs);
189 ilock(&tt->lk);
190 continue;
191 }
192
193 tt->head = t->tnext;
194 assert(t->tt == tt);
195 t->tt = nil;
196 iunlock(&tt->lk);
197 (*t->tf)(&u, t);
198 ilock(&tt->lk);
199 if(t->tmode == Tperiodic)
200 tadd(tt, t);
201 }
202 }
203
204 static void
205 kicktimerproc(void)
206 {
207 if(timer_pid != 0)
208 pthread_kill(timer_pid, SIGURG);
209 }
210
211 void
212 timersinit(void)
213 {
214 kproc("*timer*", timerkproc, nil);
215 }
216
217 static Alarms alarms;
218 static Timer alarmtimer;
219
220 static void setalarm(void);
221
222 static void
223 soundalarms(Ureg *u, Timer *t)
224 {
225 ulong now;
226 Proc *rp;
227
228 now = msec();
229 qlock(&alarms.lk);
230 while((rp = alarms.head) && rp->alarm <= now){
231 if(rp->alarm != 0){
232 if(!canqlock(&rp->debug))
233 break;
234 if(!waserror()){
235 postnote(rp, 0, "alarm", NUser);
236 poperror();
237 }
238 qunlock(&rp->debug);
239 rp->alarm = 0;
240 }
241 alarms.head = rp->palarm;
242 }
243 setalarm();
244 qunlock(&alarms.lk);
245 }
246
247 static void
248 setalarm(void)
249 {
250 Proc *p;
251 ulong now;
252
253 now = msec();
254 if(alarmtimer.tt)
255 timerdel(&alarmtimer);
256 while((p = alarms.head) && p->alarm == 0)
257 alarms.head = p->palarm;
258 if(p == nil)
259 return;
260 alarmtimer.tf = soundalarms;
261 alarmtimer.tmode = Trelative;
262 alarmtimer.tns = (p->alarm - now)*1000000LL;
263 timeradd(&alarmtimer);
264 }
265
266 ulong
267 procalarm(ulong time)
268 {
269 Proc **l, *f;
270 ulong when, old, now;
271
272 now = msec();
273 if(up->alarm)
274 old = up->alarm - now;
275 else
276 old = 0;
277 if(time == 0) {
278 up->alarm = 0;
279 return old;
280 }
281 when = time+now;
282
283 qlock(&alarms.lk);
284 l = &alarms.head;
285 for(f = *l; f; f = f->palarm) {
286 if(up == f){
287 *l = f->palarm;
288 break;
289 }
290 l = &f->palarm;
291 }
292
293 up->palarm = 0;
294 if(alarms.head) {
295 l = &alarms.head;
296 for(f = *l; f; f = f->palarm) {
297 if(f->alarm > when) {
298 up->palarm = f;
299 *l = up;
300 goto done;
301 }
302 l = &f->palarm;
303 }
304 *l = up;
305 }
306 else
307 alarms.head = up;
308 done:
309 up->alarm = when;
310 setalarm();
311 qunlock(&alarms.lk);
312
313 return old;
314 }
315
316 ulong
317 perfticks(void)
318 {
319 return msec();
320 }
321
322 // Only gets used by the profiler.
323 // Not going to bother for now.
324 Timer*
325 addclock0link(void (*x)(void), int y)
326 {
327 return 0;
328 }
329
330 uvlong
331 fastticks(uvlong *hz)
332 {
333 struct timeval tv;
334 uvlong t;
335
336 gettimeofday(&tv, 0);
337 t = tv.tv_sec * 1000000000LL + tv.tv_usec*1000LL;
338 if(hz)
339 *hz = 1000000000LL;
340 return t;
341 }
342
343 ulong
344 msec(void)
345 {
346 struct timeval tv;
347
348 gettimeofday(&tv, 0);
349 return tv.tv_sec * 1000 + tv.tv_usec/1000;
350 }
351
352 ulong
353 tk2ms(ulong x)
354 {
355 return x;
356 }
357
358 ulong
359 ms2tk(ulong x)
360 {
361 return x;
362 }
363
364 long
365 seconds(void)
366 {
367 return time(0);
368 }
369
370 void
371 todinit(void)
372 {
373 start = todget(nil);
374 }
375
376 void
377 pcycles(uvlong *t)
378 {
379 *t = fastticks(nil);
380 }
381
382 void (*cycles)(uvlong*) = pcycles;
383
384 void
385 microdelay(int x)
386 {
387 struct timeval tv;
388
389 tv.tv_sec = x/1000000;
390 tv.tv_usec = x%1000000;
391 select(0, nil, nil, nil, &tv);
392 }
393
394 /*
395 * Time of day
396 */
397 vlong
398 todget(vlong *ticksp)
399 {
400 struct timeval tv;
401 vlong t;
402 gettimeofday(&tv, NULL);
403 t = tv.tv_sec*1000000000LL + tv.tv_usec*1000LL;
404 if(ticksp)
405 *ticksp = t - start;
406 return t;
407 }
408
409 void
410 todset(vlong a, vlong b, int c)
411 {
412 USED(a);
413 USED(b);
414 USED(c);
415 }
416
417 void
418 todsetfreq(vlong a)
419 {
420 USED(a);
421 }
422