rscbsdbfb774520 years agoHistory
404 lines · 6.0 KB · C
RawDownload
#undef exits#undef _exits extern int __isthreaded; /* * spin locks */extern int _tas(int*); void_threadunlock(Lock *l, ulong pc){	USED(pc); 	l->held = 0;} int_threadlock(Lock *l, int block, ulong pc){	int i; 	USED(pc); 	/* once fast */	if(!_tas(&l->held))		return 1;	if(!block)		return 0; 	/* a thousand times pretty fast */	for(i=0; i<1000; i++){		if(!_tas(&l->held))			return 1;		sleep(0);	}	/* increasingly slow */	for(i=0; i<10; i++){		if(!_tas(&l->held))			return 1;		usleep(1);	}	for(i=0; i<10; i++){		if(!_tas(&l->held))			return 1;		usleep(10);	}	for(i=0; i<10; i++){		if(!_tas(&l->held))			return 1;		usleep(100);	}	for(i=0; i<10; i++){		if(!_tas(&l->held))			return 1;		usleep(1000);	}	for(i=0; i<10; i++){		if(!_tas(&l->held))			return 1;		usleep(10*1000);	}	/* now nice and slow */	for(i=0; i<1000; i++){		if(!_tas(&l->held))			return 1;		usleep(100*1000);	}	/* take your time */	while(_tas(&l->held))		usleep(1000*1000);	return 1;} /* * For libc. */ typedef struct {	volatile long	access_lock;	volatile long	lock_owner;	volatile char	*fname;	volatile int	lineno;} spinlock_t; void_spinlock(spinlock_t *lk){	lock((Lock*)&lk->access_lock);} void_spinunlock(spinlock_t *lk){	unlock((Lock*)&lk->access_lock);}   /* * sleep and wakeup */static voidign(int x){	USED(x);} static void /*__attribute__((constructor))*/ignusr1(int restart){	struct sigaction sa; 	memset(&sa, 0, sizeof sa);	sa.sa_handler = ign;	sigemptyset(&sa.sa_mask);	sigaddset(&sa.sa_mask, SIGUSR1);	if(restart)		sa.sa_flags = SA_RESTART;	sigaction(SIGUSR1, &sa, nil);} void_procsleep(_Procrendez *r){	sigset_t mask; 	/*	 * Go to sleep.	 *	 * Block USR1, set the handler to interrupt system calls,	 * unlock the vouslock so our waker can wake us,	 * and then suspend.	 */again:	r->asleep = 1;	r->pid = getpid(); 	sigprocmask(SIG_SETMASK, nil, &mask);	sigaddset(&mask, SIGUSR1);	sigprocmask(SIG_SETMASK, &mask, nil);	ignusr1(0);	unlock(r->l);	sigdelset(&mask, SIGUSR1);	sigsuspend(&mask); 	/*	 * We're awake.  Make USR1 not interrupt system calls.	 */	lock(r->l);	ignusr1(1);	if(r->asleep && r->pid == getpid()){		/* Didn't really wake up - signal from something else */		goto again;	}} void_procwakeup(_Procrendez *r){	if(r->asleep){		r->asleep = 0;		assert(r->pid >= 1);		kill(r->pid, SIGUSR1);	}} void_procwakeupandunlock(_Procrendez *r){	_procwakeup(r);	unlock(r->l);}  /* * process creation and exit */typedef struct Stackfree Stackfree;struct Stackfree{	Stackfree	*next;	int	pid;};static Lock stacklock;static Stackfree *stackfree; static voiddelayfreestack(uchar *stk){	Stackfree *sf; 	sf = (Stackfree*)stk;	sf->pid = getpid();	lock(&stacklock);	sf->next = stackfree;	stackfree = sf;	unlock(&stacklock);} static voiddofreestacks(void){	Stackfree *sf, *last, *next; 	if(stackfree==nil || !canlock(&stacklock))		return; 	for(last=nil,sf=stackfree; sf; last=sf,sf=next){		next = sf->next;		if(sf->pid >= 1 && kill(sf->pid, 0) < 0 && errno == ESRCH){			free(sf);			if(last)				last->next = next;			else				stackfree = next;			sf = last;		}	}	unlock(&stacklock);} static intstartprocfn(void *v){	void **a;	uchar *stk;	void (*fn)(void*);	Proc *p; 	a = (void**)v;	fn = a[0];	p = a[1];	stk = a[2];	free(a);	p->osprocid = getpid(); 	(*fn)(p); 	delayfreestack(stk);	_exit(0);	return 0;} void_procstart(Proc *p, void (*fn)(Proc*)){	void **a;	uchar *stk;	int pid; 	dofreestacks();	a = malloc(3*sizeof a[0]);	if(a == nil)		sysfatal("_procstart malloc: %r");	stk = malloc(65536);	if(stk == nil)		sysfatal("_procstart malloc stack: %r"); 	a[0] = fn;	a[1] = p;	a[2] = stk; 	pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, stk+65536-64, startprocfn, a);	if(pid < 0){		fprint(2, "_procstart rfork_thread: %r\n");		abort();	}} static char *threadexitsmsg;voidsigusr2handler(int s){/*	fprint(2, "%d usr2 %d\n", time(0), getpid()); */	if(threadexitsmsg)		_exits(threadexitsmsg);} voidthreadexitsall(char *msg){	static int pid[1024];	int i, npid, mypid;	Proc *p; 	if(msg == nil)		msg = ""; 	/* 	 * Only one guy, ever, gets to run this.	 * If two guys do it, inevitably they end up	 * tripping over each other in the underlying	 * C library exit() implementation, which is	 * trying to run the atexit handlers and apparently	 * not thread safe.  This has been observed on	 * both Linux and OpenBSD.  Sigh.	 */	{		static Lock onelock;		if(!canlock(&onelock))			_exits(threadexitsmsg);		threadexitsmsg = msg;	} 	if(msg == nil)		msg = "";	mypid = getpid();	lock(&_threadprocslock);	threadexitsmsg = msg;	npid = 0;	for(p=_threadprocs; p; p=p->next)		if(p->osprocid != mypid && p->osprocid >= 1)			pid[npid++] = p->osprocid;	for(i=0; i<npid; i++)		kill(pid[i], SIGUSR2);	unlock(&_threadprocslock);	exits(msg);} /* * per-process data, indexed by pid */typedef struct Perproc Perproc;struct Perproc{	int		pid;	Proc	*proc;}; static Lock perlock;static Perproc perproc[1024];#define P ((Proc*)-1) static Perproc*myperproc(void){	int i, pid, h;	Perproc *p; 	pid = getpid();	h = pid%nelem(perproc);	for(i=0; i<nelem(perproc); i++){		p = &perproc[(i+h)%nelem(perproc)];		if(p->pid == pid)			return p;		if(p->pid == 0){			print("found 0 at %d (h=%d)\n", (i+h)%nelem(perproc), h);			break;		}	}	fprint(2, "myperproc %d: cannot find self\n", pid);	abort();	return nil;} static Perproc*newperproc(void){	int i, pid, h;	Perproc *p; 	lock(&perlock);	pid = getpid();	h = pid%nelem(perproc);	for(i=0; i<nelem(perproc); i++){		p = &perproc[(i+h)%nelem(perproc)];		if(p->pid == pid || p->pid == -1 || p->pid == 0){			p->pid = pid;			unlock(&perlock);			return p;		}	}	fprint(2, "newperproc %d: out of procs\n", pid);	abort();	return nil;} Proc*_threadproc(void){	return myperproc()->proc;} void_threadsetproc(Proc *p){	Perproc *pp; 	if(p)		p->osprocid = getpid();	pp = newperproc();	pp->proc = p;	if(p == nil)		pp->pid = -1;} void_threadpexit(void){	_exit(0);}