plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

thread.c (16472B)


      1 #include "threadimpl.h"
      2 
      3 int	_threaddebuglevel = 0;
      4 
      5 static	uint		threadnproc;
      6 static	uint		threadnsysproc;
      7 static	Lock		threadnproclock;
      8 static	Ref		threadidref;
      9 static	Proc		*threadmainproc;
     10 
     11 static	void		addproc(Proc*);
     12 static	void		delproc(Proc*);
     13 static	void		addthread(_Threadlist*, _Thread*);
     14 static	void		delthread(_Threadlist*, _Thread*);
     15 static	int		onlist(_Threadlist*, _Thread*);
     16 static	void		addthreadinproc(Proc*, _Thread*);
     17 static	void		delthreadinproc(Proc*, _Thread*);
     18 static	void		procmain(Proc*);
     19 static	int		threadinfo(void*, char*);
     20 static 	void		pthreadscheduler(Proc *p);
     21 static	void		pthreadsleepschedlocked(Proc *p, _Thread *t);
     22 static	void		pthreadwakeupschedlocked(Proc *p, _Thread *self, _Thread *t);
     23 static	_Thread*	procnext(Proc*, _Thread*);
     24 
     25 static void
     26 _threaddebug(_Thread *t, char *fmt, ...)
     27 {
     28 	va_list arg;
     29 	char buf[128];
     30 	char *p;
     31 	static int fd = -1;
     32 
     33 	if(_threaddebuglevel == 0)
     34 		return;
     35 
     36 	if(fd < 0){
     37 		p = strrchr(argv0, '/');
     38 		if(p)
     39 			p++;
     40 		else
     41 			p = argv0;
     42 		snprint(buf, sizeof buf, "/tmp/%s.tlog", p);
     43 		if((fd = create(buf, OWRITE, 0666)) < 0)
     44 			fd = open("/dev/null", OWRITE);
     45 		if(fd >= 0 && fd != 2){
     46 			dup(fd, 2);
     47 			close(fd);
     48 			fd = 2;
     49 		}
     50 	}
     51 
     52 	va_start(arg, fmt);
     53 	vsnprint(buf, sizeof buf, fmt, arg);
     54 	va_end(arg);
     55 	if(t == nil)
     56 		t = proc()->thread;
     57 	if(t)
     58 		fprint(fd, "%p %d.%d: %s\n", proc(), getpid(), t->id, buf);
     59 	else
     60 		fprint(fd, "%p %d._: %s\n", proc(), getpid(), buf);
     61 }
     62 
     63 static _Thread*
     64 getthreadnow(void)
     65 {
     66 	return proc()->thread;
     67 }
     68 _Thread	*(*threadnow)(void) = getthreadnow;
     69 
     70 static Proc*
     71 procalloc(void)
     72 {
     73 	Proc *p;
     74 
     75 	p = malloc(sizeof *p);
     76 	if(p == nil)
     77 		sysfatal("procalloc malloc: %r");
     78 	memset(p, 0, sizeof *p);
     79 	addproc(p);
     80 	lock(&threadnproclock);
     81 	threadnproc++;
     82 	unlock(&threadnproclock);
     83 	return p;
     84 }
     85 
     86 _Thread*
     87 _threadcreate(Proc *p, void (*fn)(void*), void *arg, uint stack)
     88 {
     89 	_Thread *t;
     90 
     91 	USED(stack);
     92 	t = malloc(sizeof *t);
     93 	if(t == nil)
     94 		sysfatal("threadcreate malloc: %r");
     95 	memset(t, 0, sizeof *t);
     96 	t->id = incref(&threadidref);
     97 	t->startfn = fn;
     98 	t->startarg = arg;
     99 	t->proc = p;
    100 	if(p->nthread != 0)
    101 		_threadpthreadstart(p, t);
    102 	else
    103 		t->mainthread = 1;
    104 	p->nthread++;
    105 	addthreadinproc(p, t);
    106 	_threadready(t);
    107 	return t;
    108 }
    109 
    110 int
    111 threadcreate(void (*fn)(void*), void *arg, uint stack)
    112 {
    113 	_Thread *t;
    114 
    115 	t = _threadcreate(proc(), fn, arg, stack);
    116 	_threaddebug(nil, "threadcreate %d", t->id);
    117 	return t->id;
    118 }
    119 
    120 int
    121 proccreate(void (*fn)(void*), void *arg, uint stack)
    122 {
    123 	int id;
    124 	_Thread *t;
    125 	Proc *p;
    126 
    127 	p = procalloc();
    128 	t = _threadcreate(p, fn, arg, stack);
    129 	id = t->id;	/* t might be freed after _procstart */
    130 	_threaddebug(t, "proccreate %p", p);
    131 	_procstart(p, procmain);
    132 	return id;
    133 }
    134 
    135 void
    136 _threadswitch(void)
    137 {
    138 	Proc *p;
    139 
    140 	needstack(0);
    141 	p = proc();
    142 /*print("threadswtch %p\n", p); */
    143 	pthreadscheduler(p);
    144 }
    145 
    146 void
    147 _threadready(_Thread *t)
    148 {
    149 	Proc *p;
    150 
    151 	p = t->proc;
    152 	lock(&p->lock);
    153 	p->runrend.l = &p->lock;
    154 	addthread(&p->runqueue, t);
    155 /*print("%d wake for job %d->%d\n", time(0), getpid(), p->osprocid); */
    156 	if(p != proc())
    157 		_procwakeupandunlock(&p->runrend);
    158 	else
    159 		unlock(&p->lock);
    160 }
    161 
    162 int
    163 threadidle(void)
    164 {
    165 	int n;
    166 	Proc *p;
    167 
    168 	p = proc();
    169 	n = p->nswitch;
    170 	lock(&p->lock);
    171 	p->runrend.l = &p->lock;
    172 	addthread(&p->idlequeue, p->thread);
    173 	unlock(&p->lock);
    174 	_threadswitch();
    175 	return p->nswitch - n;
    176 }
    177 
    178 int
    179 threadyield(void)
    180 {
    181 	int n;
    182 	Proc *p;
    183 
    184 	p = proc();
    185 	n = p->nswitch;
    186 	_threadready(p->thread);
    187 	_threadswitch();
    188 	return p->nswitch - n;
    189 }
    190 
    191 void
    192 threadexits(char *msg)
    193 {
    194 	Proc *p;
    195 
    196 	p = proc();
    197 	if(msg == nil)
    198 		msg = "";
    199 	utfecpy(p->msg, p->msg+sizeof p->msg, msg);
    200 	proc()->thread->exiting = 1;
    201 	_threadswitch();
    202 }
    203 
    204 void
    205 threadpin(void)
    206 {
    207 	Proc *p;
    208 
    209 	p = proc();
    210 	if(p->pinthread){
    211 		fprint(2, "already pinning a thread - %p %p\n", p->pinthread, p->thread);
    212 		assert(0);
    213 	}
    214 	p->pinthread = p->thread;
    215 }
    216 
    217 void
    218 threadunpin(void)
    219 {
    220 	Proc *p;
    221 
    222 	p = proc();
    223 	if(p->pinthread != p->thread){
    224 		fprint(2, "wrong pinthread - %p %p\n", p->pinthread, p->thread);
    225 		assert(0);
    226 	}
    227 	p->pinthread = nil;
    228 }
    229 
    230 void
    231 threadsysfatal(char *fmt, va_list arg)
    232 {
    233 	char buf[256];
    234 
    235 	vseprint(buf, buf+sizeof(buf), fmt, arg);
    236 	__fixargv0();
    237 	fprint(2, "%s: %s\n", argv0 ? argv0 : "<prog>", buf);
    238 	threadexitsall(buf);
    239 }
    240 
    241 static void
    242 procmain(Proc *p)
    243 {
    244 	_Thread *t;
    245 
    246 	_threadsetproc(p);
    247 
    248 	/* take out first thread to run on system stack */
    249 	t = p->runqueue.head;
    250 	delthread(&p->runqueue, t);
    251 
    252 	/* run it */
    253 	p->thread = t;
    254 	t->startfn(t->startarg);
    255 	if(p->nthread != 0)
    256 		threadexits(nil);
    257 }
    258 
    259 void
    260 _threadpthreadmain(Proc *p, _Thread *t)
    261 {
    262 	_threadsetproc(p);
    263 	lock(&p->lock);
    264 	pthreadsleepschedlocked(p, t);
    265 	unlock(&p->lock);
    266 	_threaddebug(nil, "startfn");
    267 	t->startfn(t->startarg);
    268 	threadexits(nil);
    269 }
    270 
    271 static void
    272 pthreadsleepschedlocked(Proc *p, _Thread *t)
    273 {
    274 	_threaddebug(t, "pthreadsleepsched %p %d", p, t->id);;
    275 	t->schedrend.l = &p->lock;
    276 	while(p->schedthread != t)
    277 		_procsleep(&t->schedrend);
    278 }
    279 
    280 static void
    281 pthreadwakeupschedlocked(Proc *p, _Thread *self, _Thread *t)
    282 {
    283 	_threaddebug(self, "pthreadwakeupschedlocked %p %d", p, t->id);;
    284 	t->schedrend.l = &p->lock;
    285 	p->schedthread = t;
    286 	_procwakeup(&t->schedrend);
    287 }
    288 
    289 static void
    290 pthreadscheduler(Proc *p)
    291 {
    292 	_Thread *self, *t;
    293 
    294 	_threaddebug(nil, "scheduler");
    295 	lock(&p->lock);
    296 	self = p->thread;
    297 	p->thread = nil;
    298 	_threaddebug(self, "pausing");
    299 
    300 	if(self->exiting) {
    301 		_threaddebug(self, "exiting");
    302 		delthreadinproc(p, self);
    303 		p->nthread--;
    304 	}
    305 
    306 	t = procnext(p, self);
    307 	if(t != nil) {
    308 		pthreadwakeupschedlocked(p, self, t);
    309 		if(!self->exiting) {
    310 			pthreadsleepschedlocked(p, self);
    311 			_threaddebug(nil, "resume %d", self->id);
    312 			unlock(&p->lock);
    313 			return;
    314 		}
    315 	}
    316 
    317 	if(t == nil) {
    318 		/* Tear down proc bookkeeping. Wait to free p. */
    319 		delproc(p);
    320 		lock(&threadnproclock);
    321 		if(p->sysproc)
    322 			--threadnsysproc;
    323 		if(--threadnproc == threadnsysproc)
    324 			threadexitsall(p->msg);
    325 		unlock(&threadnproclock);
    326 	}
    327 
    328 	/* Tear down pthread. */
    329 	if(self->mainthread && p->mainproc) {
    330 		_threaddaemonize();
    331 		_threaddebug(self, "sleeper");
    332 		unlock(&p->lock);
    333 		/*
    334 		 * Avoid bugs with main pthread exiting.
    335 		 * When all procs are gone, threadexitsall above will happen.
    336 		 */
    337 		for(;;)
    338 			sleep(60*60*1000);
    339 	}
    340 	_threadsetproc(nil);
    341 	free(self);
    342 	unlock(&p->lock);
    343 	if(t == nil)
    344 		free(p);
    345 	_threadpexit();
    346 }
    347 
    348 static _Thread*
    349 procnext(Proc *p, _Thread *self)
    350 {
    351 	_Thread *t;
    352 
    353 	if((t = p->pinthread) != nil){
    354 		while(!onlist(&p->runqueue, t)){
    355 			p->runrend.l = &p->lock;
    356 			_threaddebug(self, "scheduler sleep (pin)");
    357 			_procsleep(&p->runrend);
    358 			_threaddebug(self, "scheduler wake (pin)");
    359 		}
    360 	} else
    361 	while((t = p->runqueue.head) == nil){
    362 		if(p->nthread == 0)
    363 			return nil;
    364 		if((t = p->idlequeue.head) != nil){
    365 			/*
    366 			 * Run all the idling threads once.
    367 			 */
    368 			while((t = p->idlequeue.head) != nil){
    369 				delthread(&p->idlequeue, t);
    370 				addthread(&p->runqueue, t);
    371 			}
    372 			continue;
    373 		}
    374 		p->runrend.l = &p->lock;
    375 		_threaddebug(self, "scheduler sleep");
    376 		_procsleep(&p->runrend);
    377 		_threaddebug(self, "scheduler wake");
    378 	}
    379 
    380 	if(p->pinthread && p->pinthread != t)
    381 		fprint(2, "p->pinthread %p t %p\n", p->pinthread, t);
    382 	assert(p->pinthread == nil || p->pinthread == t);
    383 	delthread(&p->runqueue, t);
    384 
    385 	p->thread = t;
    386 	p->nswitch++;
    387 	return t;
    388 }
    389 
    390 void
    391 _threadsetsysproc(void)
    392 {
    393 	lock(&threadnproclock);
    394 	if(++threadnsysproc == threadnproc)
    395 		threadexitsall(nil);
    396 	unlock(&threadnproclock);
    397 	proc()->sysproc = 1;
    398 }
    399 
    400 void**
    401 procdata(void)
    402 {
    403 	return &proc()->udata;
    404 }
    405 
    406 void**
    407 threaddata(void)
    408 {
    409 	return &proc()->thread->udata;
    410 }
    411 
    412 extern Jmp *(*_notejmpbuf)(void);
    413 static Jmp*
    414 threadnotejmp(void)
    415 {
    416 	return &proc()->sigjmp;
    417 }
    418 
    419 /*
    420  * debugging
    421  */
    422 void
    423 threadsetname(char *fmt, ...)
    424 {
    425 	va_list arg;
    426 	_Thread *t;
    427 
    428 	t = proc()->thread;
    429 	va_start(arg, fmt);
    430 	vsnprint(t->name, sizeof t->name, fmt, arg);
    431 	va_end(arg);
    432 }
    433 
    434 char*
    435 threadgetname(void)
    436 {
    437 	return proc()->thread->name;
    438 }
    439 
    440 void
    441 threadsetstate(char *fmt, ...)
    442 {
    443 	va_list arg;
    444 	_Thread *t;
    445 
    446 	t = proc()->thread;
    447 	va_start(arg, fmt);
    448 	vsnprint(t->state, sizeof t->name, fmt, arg);
    449 	va_end(arg);
    450 }
    451 
    452 int
    453 threadid(void)
    454 {
    455 	_Thread *t;
    456 
    457 	t = proc()->thread;
    458 	return t->id;
    459 }
    460 
    461 void
    462 needstack(int n)
    463 {
    464 	_Thread *t;
    465 
    466 	t = proc()->thread;
    467 	if(t->stk == nil)
    468 		return;
    469 
    470 	if((char*)&t <= (char*)t->stk
    471 	|| (char*)&t - (char*)t->stk < 256+n){
    472 		fprint(2, "thread stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n);
    473 		abort();
    474 	}
    475 }
    476 
    477 static int
    478 singlethreaded(void)
    479 {
    480 	return threadnproc == 1 && _threadprocs->nthread == 1;
    481 }
    482 
    483 /*
    484  * locking
    485  */
    486 static int
    487 threadqlock(QLock *l, int block, ulong pc)
    488 {
    489 /*print("threadqlock %p\n", l); */
    490 	lock(&l->l);
    491 	if(l->owner == nil){
    492 		l->owner = (*threadnow)();
    493 /*print("qlock %p @%#x by %p\n", l, pc, l->owner); */
    494 		if(l->owner == nil) {
    495 			fprint(2, "%s: qlock uncontended owner=nil oops\n", argv0);
    496 			abort();
    497 		}
    498 		unlock(&l->l);
    499 		return 1;
    500 	}
    501 	if(!block){
    502 		unlock(&l->l);
    503 		return 0;
    504 	}
    505 
    506 	if(singlethreaded()){
    507 		fprint(2, "qlock deadlock\n");
    508 		abort();
    509 	}
    510 
    511 /*print("qsleep %p @%#x by %p\n", l, pc, (*threadnow)()); */
    512 	addthread(&l->waiting, (*threadnow)());
    513 	unlock(&l->l);
    514 
    515 	_threadswitch();
    516 
    517 	if(l->owner != (*threadnow)()){
    518 		fprint(2, "%s: qlock pc=0x%lux owner=%p self=%p oops\n",
    519 			argv0, pc, l->owner, (*threadnow)());
    520 		abort();
    521 	}
    522 	if(l->owner == nil) {
    523 		fprint(2, "%s: qlock threadswitch owner=nil oops\n", argv0);
    524 		abort();
    525 	}
    526 
    527 /*print("qlock wakeup %p @%#x by %p\n", l, pc, (*threadnow)()); */
    528 	return 1;
    529 }
    530 
    531 static void
    532 threadqunlock(QLock *l, ulong pc)
    533 {
    534 	_Thread *ready;
    535 
    536 	lock(&l->l);
    537 /*print("qlock unlock %p @%#x by %p (owner %p)\n", l, pc, (*threadnow)(), l->owner); */
    538 	if(l->owner == 0){
    539 		fprint(2, "%s: qunlock pc=0x%lux owner=%p self=%p oops\n",
    540 			argv0, pc, l->owner, (*threadnow)());
    541 		abort();
    542 	}
    543 	if((l->owner = ready = l->waiting.head) != nil)
    544 		delthread(&l->waiting, l->owner);
    545 	/*
    546 	 * N.B. Cannot call _threadready() before unlocking l->l,
    547 	 * because the thread we are readying might:
    548 	 *	- be in another proc
    549 	 *	- start running immediately
    550 	 *	- and free l before we get a chance to run again
    551 	 */
    552 	unlock(&l->l);
    553 	if(ready)
    554 		_threadready(l->owner);
    555 }
    556 
    557 static int
    558 threadrlock(RWLock *l, int block, ulong pc)
    559 {
    560 	USED(pc);
    561 
    562 	lock(&l->l);
    563 	if(l->writer == nil && l->wwaiting.head == nil){
    564 		l->readers++;
    565 		unlock(&l->l);
    566 		return 1;
    567 	}
    568 	if(!block){
    569 		unlock(&l->l);
    570 		return 0;
    571 	}
    572 	if(singlethreaded()){
    573 		fprint(2, "rlock deadlock\n");
    574 		abort();
    575 	}
    576 	addthread(&l->rwaiting, (*threadnow)());
    577 	unlock(&l->l);
    578 	_threadswitch();
    579 	return 1;
    580 }
    581 
    582 static int
    583 threadwlock(RWLock *l, int block, ulong pc)
    584 {
    585 	USED(pc);
    586 
    587 	lock(&l->l);
    588 	if(l->writer == nil && l->readers == 0){
    589 		l->writer = (*threadnow)();
    590 		unlock(&l->l);
    591 		return 1;
    592 	}
    593 	if(!block){
    594 		unlock(&l->l);
    595 		return 0;
    596 	}
    597 	if(singlethreaded()){
    598 		fprint(2, "wlock deadlock\n");
    599 		abort();
    600 	}
    601 	addthread(&l->wwaiting, (*threadnow)());
    602 	unlock(&l->l);
    603 	_threadswitch();
    604 	return 1;
    605 }
    606 
    607 static void
    608 threadrunlock(RWLock *l, ulong pc)
    609 {
    610 	_Thread *t;
    611 
    612 	USED(pc);
    613 	t = nil;
    614 	lock(&l->l);
    615 	--l->readers;
    616 	if(l->readers == 0 && (t = l->wwaiting.head) != nil){
    617 		delthread(&l->wwaiting, t);
    618 		l->writer = t;
    619 	}
    620 	unlock(&l->l);
    621 	if(t)
    622 		_threadready(t);
    623 
    624 }
    625 
    626 static void
    627 threadwunlock(RWLock *l, ulong pc)
    628 {
    629 	_Thread *t;
    630 
    631 	USED(pc);
    632 	lock(&l->l);
    633 	l->writer = nil;
    634 	assert(l->readers == 0);
    635 	while((t = l->rwaiting.head) != nil){
    636 		delthread(&l->rwaiting, t);
    637 		l->readers++;
    638 		_threadready(t);
    639 	}
    640 	t = nil;
    641 	if(l->readers == 0 && (t = l->wwaiting.head) != nil){
    642 		delthread(&l->wwaiting, t);
    643 		l->writer = t;
    644 	}
    645 	unlock(&l->l);
    646 	if(t)
    647 		_threadready(t);
    648 }
    649 
    650 /*
    651  * sleep and wakeup
    652  */
    653 static void
    654 threadrsleep(Rendez *r, ulong pc)
    655 {
    656 	if(singlethreaded()){
    657 		fprint(2, "rsleep deadlock\n");
    658 		abort();
    659 	}
    660 	addthread(&r->waiting, proc()->thread);
    661 	qunlock(r->l);
    662 	_threadswitch();
    663 	qlock(r->l);
    664 }
    665 
    666 static int
    667 threadrwakeup(Rendez *r, int all, ulong pc)
    668 {
    669 	int i;
    670 	_Thread *t;
    671 
    672 	_threaddebug(nil, "rwakeup %p %d", r, all);
    673 	for(i=0;; i++){
    674 		if(i==1 && !all)
    675 			break;
    676 		if((t = r->waiting.head) == nil)
    677 			break;
    678 		_threaddebug(nil, "rwakeup %p %d -> wake %d", r, all, t->id);
    679 		delthread(&r->waiting, t);
    680 		_threadready(t);
    681 		_threaddebug(nil, "rwakeup %p %d -> loop", r, all);
    682 	}
    683 	_threaddebug(nil, "rwakeup %p %d -> total %d", r, all, i);
    684 	return i;
    685 }
    686 
    687 /*
    688  * startup
    689  */
    690 
    691 static int threadargc;
    692 static char **threadargv;
    693 int mainstacksize;
    694 extern int _p9usepwlibrary;	/* getgrgid etc. smash the stack - tell _p9dir just say no */
    695 static void
    696 threadmainstart(void *v)
    697 {
    698 	USED(v);
    699 
    700 	/*
    701 	 * N.B. This call to proc() is a program's first call (indirectly) to a
    702 	 * pthreads function while executing on a non-pthreads-allocated
    703 	 * stack.  If the pthreads implementation is using the stack pointer
    704 	 * to locate the per-thread data, then this call will blow up.
    705 	 * This means the pthread implementation is not suitable for
    706 	 * running under libthread.  Time to write your own.  Sorry.
    707 	 */
    708 	_p9usepwlibrary = 0;
    709 	threadmainproc = proc();
    710 	threadmain(threadargc, threadargv);
    711 }
    712 
    713 extern void (*_sysfatal)(char*, va_list);
    714 
    715 int
    716 main(int argc, char **argv)
    717 {
    718 	Proc *p;
    719 	_Thread *t;
    720 	char *opts;
    721 
    722 	argv0 = argv[0];
    723 
    724 	opts = getenv("LIBTHREAD");
    725 	if(opts == nil)
    726 		opts = "";
    727 
    728 	if(threadmaybackground() && strstr(opts, "nodaemon") == nil && getenv("NOLIBTHREADDAEMONIZE") == nil)
    729 		_threadsetupdaemonize();
    730 
    731 	threadargc = argc;
    732 	threadargv = argv;
    733 
    734 	/*
    735 	 * Install locking routines into C library.
    736 	 */
    737 	_lock = _threadlock;
    738 	_unlock = _threadunlock;
    739 	_qlock = threadqlock;
    740 	_qunlock = threadqunlock;
    741 	_rlock = threadrlock;
    742 	_runlock = threadrunlock;
    743 	_wlock = threadwlock;
    744 	_wunlock = threadwunlock;
    745 	_rsleep = threadrsleep;
    746 	_rwakeup = threadrwakeup;
    747 	_notejmpbuf = threadnotejmp;
    748 	_pin = threadpin;
    749 	_unpin = threadunpin;
    750 	_sysfatal = threadsysfatal;
    751 
    752 	_pthreadinit();
    753 	p = procalloc();
    754 	p->mainproc = 1;
    755 	_threadsetproc(p);
    756 	if(mainstacksize == 0)
    757 		mainstacksize = 256*1024;
    758 	atnotify(threadinfo, 1);
    759 	t = _threadcreate(p, threadmainstart, nil, mainstacksize);
    760 	t->mainthread = 1;
    761 	procmain(p);
    762 	sysfatal("procmain returned in libthread");
    763 	/* does not return */
    764 	return 0;
    765 }
    766 
    767 /*
    768  * hooray for linked lists
    769  */
    770 static void
    771 addthread(_Threadlist *l, _Thread *t)
    772 {
    773 	if(l->tail){
    774 		l->tail->next = t;
    775 		t->prev = l->tail;
    776 	}else{
    777 		l->head = t;
    778 		t->prev = nil;
    779 	}
    780 	l->tail = t;
    781 	t->next = nil;
    782 }
    783 
    784 static void
    785 delthread(_Threadlist *l, _Thread *t)
    786 {
    787 	if(t->prev)
    788 		t->prev->next = t->next;
    789 	else
    790 		l->head = t->next;
    791 	if(t->next)
    792 		t->next->prev = t->prev;
    793 	else
    794 		l->tail = t->prev;
    795 }
    796 
    797 /* inefficient but rarely used */
    798 static int
    799 onlist(_Threadlist *l, _Thread *t)
    800 {
    801 	_Thread *tt;
    802 
    803 	for(tt = l->head; tt; tt=tt->next)
    804 		if(tt == t)
    805 			return 1;
    806 	return 0;
    807 }
    808 
    809 static void
    810 addthreadinproc(Proc *p, _Thread *t)
    811 {
    812 	_Threadlist *l;
    813 
    814 	l = &p->allthreads;
    815 	if(l->tail){
    816 		l->tail->allnext = t;
    817 		t->allprev = l->tail;
    818 	}else{
    819 		l->head = t;
    820 		t->allprev = nil;
    821 	}
    822 	l->tail = t;
    823 	t->allnext = nil;
    824 }
    825 
    826 static void
    827 delthreadinproc(Proc *p, _Thread *t)
    828 {
    829 	_Threadlist *l;
    830 
    831 	l = &p->allthreads;
    832 	if(t->allprev)
    833 		t->allprev->allnext = t->allnext;
    834 	else
    835 		l->head = t->allnext;
    836 	if(t->allnext)
    837 		t->allnext->allprev = t->allprev;
    838 	else
    839 		l->tail = t->allprev;
    840 }
    841 
    842 Proc *_threadprocs;
    843 Lock _threadprocslock;
    844 static Proc *_threadprocstail;
    845 
    846 static void
    847 addproc(Proc *p)
    848 {
    849 	lock(&_threadprocslock);
    850 	if(_threadprocstail){
    851 		_threadprocstail->next = p;
    852 		p->prev = _threadprocstail;
    853 	}else{
    854 		_threadprocs = p;
    855 		p->prev = nil;
    856 	}
    857 	_threadprocstail = p;
    858 	p->next = nil;
    859 	unlock(&_threadprocslock);
    860 }
    861 
    862 static void
    863 delproc(Proc *p)
    864 {
    865 	lock(&_threadprocslock);
    866 	if(p->prev)
    867 		p->prev->next = p->next;
    868 	else
    869 		_threadprocs = p->next;
    870 	if(p->next)
    871 		p->next->prev = p->prev;
    872 	else
    873 		_threadprocstail = p->prev;
    874 	unlock(&_threadprocslock);
    875 }
    876 
    877 /*
    878  * notify - for now just use the usual mechanisms
    879  */
    880 void
    881 threadnotify(int (*f)(void*, char*), int in)
    882 {
    883 	atnotify(f, in);
    884 }
    885 
    886 static int
    887 onrunqueue(Proc *p, _Thread *t)
    888 {
    889 	_Thread *tt;
    890 
    891 	for(tt=p->runqueue.head; tt; tt=tt->next)
    892 		if(tt == t)
    893 			return 1;
    894 	return 0;
    895 }
    896 
    897 /*
    898  * print state - called from SIGINFO
    899  */
    900 static int
    901 threadinfo(void *v, char *s)
    902 {
    903 	Proc *p;
    904 	_Thread *t;
    905 
    906 	if(strcmp(s, "quit") != 0 && strcmp(s, "sys: status request") != 0)
    907 		return 0;
    908 
    909 	for(p=_threadprocs; p; p=p->next){
    910 		fprint(2, "proc %p %s%s\n", (void*)p->osprocid, p->msg,
    911 			p->sysproc ? " (sysproc)": "");
    912 		for(t=p->allthreads.head; t; t=t->allnext){
    913 			fprint(2, "\tthread %d %s: %s %s\n",
    914 				t->id,
    915 				t == p->thread ? "Running" :
    916 				onrunqueue(p, t) ? "Ready" : "Sleeping",
    917 				t->state, t->name);
    918 		}
    919 	}
    920 	return 1;
    921 }