907 lines · 16.2 KB · C
RawDownload
#include "threadimpl.h" int	_threaddebuglevel; static	uint		threadnproc;static	uint		threadnsysproc;static	Lock		threadnproclock;static	Ref		threadidref;static	Proc		*threadmainproc; static	void		addproc(Proc*);static	void		delproc(Proc*);static	void		addthread(_Threadlist*, _Thread*);static	void		delthread(_Threadlist*, _Thread*);static	int		onlist(_Threadlist*, _Thread*);static	void		addthreadinproc(Proc*, _Thread*);static	void		delthreadinproc(Proc*, _Thread*);static	void		contextswitch(Context *from, Context *to);static	void		procscheduler(Proc*);static	int		threadinfo(void*, char*); static void_threaddebug(char *fmt, ...){	va_list arg;	char buf[128];	_Thread *t;	char *p;	static int fd = -1; 	if(_threaddebuglevel == 0)		return; 	if(fd < 0){		p = strrchr(argv0, '/');		if(p)			p++;		else			p = argv0;		snprint(buf, sizeof buf, "/tmp/%s.tlog", p);		if((fd = create(buf, OWRITE, 0666)) < 0)			fd = open("/dev/null", OWRITE);		if(fd >= 0 && fd != 2){			dup(fd, 2);			close(fd);			fd = 2;		}	} 	va_start(arg, fmt);	vsnprint(buf, sizeof buf, fmt, arg);	va_end(arg);	t = proc()->thread;	if(t)		fprint(fd, "%d.%d: %s\n", getpid(), t->id, buf);	else		fprint(fd, "%d._: %s\n", getpid(), buf);} static _Thread*getthreadnow(void){	return proc()->thread;}_Thread	*(*threadnow)(void) = getthreadnow; static Proc*procalloc(void){	Proc *p; 	p = malloc(sizeof *p);	if(p == nil)		sysfatal("procalloc malloc: %r");	memset(p, 0, sizeof *p);	addproc(p);	lock(&threadnproclock);	threadnproc++;	unlock(&threadnproclock);	return p;} static voidthreadstart(uint y, uint x){	_Thread *t;	ulong z; 	z = (ulong)x << 16;	/* hide undefined 32-bit shift from 32-bit compilers */	z <<= 16;	z |= y;	t = (_Thread*)z; /*print("threadstart %p\n", v); */	t->startfn(t->startarg);/*print("threadexits %p\n", v); */	threadexits(nil);/*print("not reacehd\n"); */} static _Thread*threadalloc(void (*fn)(void*), void *arg, uint stack){	_Thread *t;	sigset_t zero;	uint x, y;	ulong z; 	/* allocate the task and stack together */	t = malloc(sizeof *t+stack);	if(t == nil)		sysfatal("threadalloc malloc: %r");	memset(t, 0, sizeof *t);	t->stk = (uchar*)(t+1);	t->stksize = stack;	t->id = incref(&threadidref);	t->startfn = fn;	t->startarg = arg; 	/* do a reasonable initialization */	memset(&t->context.uc, 0, sizeof t->context.uc);	sigemptyset(&zero);	sigprocmask(SIG_BLOCK, &zero, &t->context.uc.uc_sigmask); 	/* must initialize with current context */	if(getcontext(&t->context.uc) < 0)		sysfatal("threadalloc getcontext: %r"); 	/* call makecontext to do the real work. */	/* leave a few words open on both ends */	t->context.uc.uc_stack.ss_sp = (void*)(t->stk+8);	t->context.uc.uc_stack.ss_size = t->stksize-64;#if defined(__sun__) && !defined(__MAKECONTEXT_V2_SOURCE)		/* sigh */	/* can avoid this with __MAKECONTEXT_V2_SOURCE but only on SunOS 5.9 */	t->context.uc.uc_stack.ss_sp = 		(char*)t->context.uc.uc_stack.ss_sp		+t->context.uc.uc_stack.ss_size;#endif	/*	 * All this magic is because you have to pass makecontext a	 * function that takes some number of word-sized variables,	 * and on 64-bit machines pointers are bigger than words.	 */	z = (ulong)t;	y = z;	z >>= 16;	/* hide undefined 32-bit shift from 32-bit compilers */	x = z>>16;	makecontext(&t->context.uc, (void(*)(void))threadstart, 2, y, x); 	return t;} _Thread*_threadcreate(Proc *p, void (*fn)(void*), void *arg, uint stack){	_Thread *t; 	/* defend against bad C libraries */	if(stack < (256<<10))		stack = 256<<10;		t = threadalloc(fn, arg, stack);	t->proc = p;	addthreadinproc(p, t);	p->nthread++;	_threadready(t);	return t;} intthreadcreate(void (*fn)(void*), void *arg, uint stack){	_Thread *t; 	t = _threadcreate(proc(), fn, arg, stack);	return t->id;} intproccreate(void (*fn)(void*), void *arg, uint stack){	int id;	_Thread *t;	Proc *p; 	p = procalloc();	t = _threadcreate(p, fn, arg, stack);	id = t->id;	/* t might be freed after _procstart */	_procstart(p, procscheduler);	return id;} void_threadswitch(void){	Proc *p; 	needstack(0);	p = proc();/*print("threadswtch %p\n", p); */	contextswitch(&p->thread->context, &p->schedcontext);} void_threadready(_Thread *t){	Proc *p; 	p = t->proc;	lock(&p->lock);	p->runrend.l = &p->lock;	addthread(&p->runqueue, t);/*print("%d wake for job %d->%d\n", time(0), getpid(), p->osprocid); */	if(p != proc())		_procwakeupandunlock(&p->runrend);	else		unlock(&p->lock);} intthreadidle(void){	int n;	Proc *p;		p = proc();	n = p->nswitch;	lock(&p->lock);	p->runrend.l = &p->lock;	addthread(&p->idlequeue, p->thread);	unlock(&p->lock);	_threadswitch();	return p->nswitch - n;} intthreadyield(void){	int n;	Proc *p; 	p = proc();	n = p->nswitch;	_threadready(p->thread);	_threadswitch();	return p->nswitch - n;} voidthreadexits(char *msg){	Proc *p; 	p = proc();	if(msg == nil)		msg = "";	utfecpy(p->msg, p->msg+sizeof p->msg, msg);	proc()->thread->exiting = 1;	_threadswitch();} voidthreadpin(void){	Proc *p; 	p = proc();	if(p->pinthread){		fprint(2, "already pinning a thread - %p %p\n", p->pinthread, p->thread);		assert(0);	}	p->pinthread = p->thread;} voidthreadunpin(void){	Proc *p; 	p = proc();	if(p->pinthread != p->thread){		fprint(2, "wrong pinthread - %p %p\n", p->pinthread, p->thread);		assert(0);	}	p->pinthread = nil;} voidthreadsysfatal(char *fmt, va_list arg){	char buf[256]; 	vseprint(buf, buf+sizeof(buf), fmt, arg);	__fixargv0();	fprint(2, "%s: %s\n", argv0 ? argv0 : "<prog>", buf);	threadexitsall(buf);} static voidcontextswitch(Context *from, Context *to){	if(swapcontext(&from->uc, &to->uc) < 0){		fprint(2, "swapcontext failed: %r\n");		assert(0);	}} static voidprocscheduler(Proc *p){	_Thread *t; 	setproc(p);	_threaddebug("scheduler enter");/*	print("s %p\n", p); */	lock(&p->lock);	for(;;){		if((t = p->pinthread) != nil){			while(!onlist(&p->runqueue, t)){				p->runrend.l = &p->lock;				_threaddebug("scheduler sleep (pin)");				_procsleep(&p->runrend);				_threaddebug("scheduler wake (pin)");			}		}else		while((t = p->runqueue.head) == nil){			if(p->nthread == 0)				goto Out;			if((t = p->idlequeue.head) != nil){				/*				 * Run all the idling threads once.				 */				while((t = p->idlequeue.head) != nil){					delthread(&p->idlequeue, t);					addthread(&p->runqueue, t);				}				continue;			}			p->runrend.l = &p->lock;			_threaddebug("scheduler sleep");			_procsleep(&p->runrend);			_threaddebug("scheduler wake");		}		if(p->pinthread && p->pinthread != t)			fprint(2, "p->pinthread %p t %p\n", p->pinthread, t);		assert(p->pinthread == nil || p->pinthread == t);		delthread(&p->runqueue, t);		unlock(&p->lock);		p->thread = t;		p->nswitch++;		_threaddebug("run %d (%s)", t->id, t->name);		contextswitch(&p->schedcontext, &t->context);/*print("back in scheduler\n"); */		p->thread = nil;		lock(&p->lock);		if(t->exiting){			delthreadinproc(p, t);			p->nthread--;/*print("nthread %d\n", p->nthread); */			free(t);		}	} Out:	_threaddebug("scheduler exit");	if(p->mainproc){		/*		 * Stupid bug - on Linux 2.6 and maybe elsewhere,		 * if the main thread exits then the others keep running		 * but the process shows up as a zombie in ps and is not		 * attachable with ptrace.  We'll just sit around pretending		 * to be a system proc instead of exiting.		 */		_threaddaemonize();		lock(&threadnproclock);		if(++threadnsysproc == threadnproc)			threadexitsall(p->msg);		p->sysproc = 1;		unlock(&threadnproclock);		for(;;)		 	sleep(1000);	} 	delproc(p);	lock(&threadnproclock);	if(p->sysproc)		--threadnsysproc;	if(--threadnproc == threadnsysproc)		threadexitsall(p->msg);	unlock(&threadnproclock);	unlock(&p->lock);	_threadsetproc(nil);	free(p);} void_threadsetsysproc(void){	lock(&threadnproclock);	if(++threadnsysproc == threadnproc)		threadexitsall(nil);	unlock(&threadnproclock);	proc()->sysproc = 1;} void**procdata(void){	return &proc()->udata;} void**threaddata(void){	return &proc()->thread->udata;} extern Jmp *(*_notejmpbuf)(void);static Jmp*threadnotejmp(void){	return &proc()->sigjmp;} /* * debugging */voidthreadsetname(char *fmt, ...){	va_list arg;	_Thread *t; 	t = proc()->thread;	va_start(arg, fmt);	vsnprint(t->name, sizeof t->name, fmt, arg);	va_end(arg);} char*threadgetname(void){	return proc()->thread->name;} voidthreadsetstate(char *fmt, ...){	va_list arg;	_Thread *t; 	t = proc()->thread;	va_start(arg, fmt);	vsnprint(t->state, sizeof t->name, fmt, arg);	va_end(arg);} intthreadid(void){	_Thread *t;		t = proc()->thread;	return t->id;} voidneedstack(int n){	_Thread *t; 	t = proc()->thread; 	if((char*)&t <= (char*)t->stk	|| (char*)&t - (char*)t->stk < 256+n){		fprint(2, "thread stack overflow: &t=%p tstk=%p n=%d\n", &t, t->stk, 256+n);		abort();	}} static intsinglethreaded(void){	return threadnproc == 1 && _threadprocs->nthread == 1;} /* * locking */static intthreadqlock(QLock *l, int block, ulong pc){/*print("threadqlock %p\n", l); */	lock(&l->l);	if(l->owner == nil){		l->owner = (*threadnow)();/*print("qlock %p @%#x by %p\n", l, pc, l->owner); */		unlock(&l->l);		return 1;	}

Lines 1–500 of 907.