vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | 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