henesyinit from p9f0ed2a895 years agoHistory
1157 lines · 24.8 KB · C
RawDownload
#include <u.h>#include <libc.h>#include "assert.h"#include "threadimpl.h" static void		initproc(void (*)(ulong, int, char*[]), int, char*[], uint);static void		garbagethread(Thread *t);static Thread*	getqbytag(Tqueue *, ulong);static void		putq(Tqueue *, Thread *);static Thread*	getq(Tqueue *);static void		launcher(ulong, void (*)(void *), void *);static void		mainlauncher(ulong, int argc, char *argv[]);static void		garbageproc(Proc *);static Proc*	prepproc(Newproc *);static int		setnoteid(int);static int		getnoteid(void); static int		notefd;static int		passerpid;static long	totalmalloc; static Thread*	dontkill; static Tqueue	rendez;static Lock	rendezlock;int			mainstacksize;Channel*		thewaitchan; struct Pqueue	pq;Proc			**procp;	/* Pointer to pointer to proc's Proc structure */ #define STACKOFF 2#ifdef M386	#define FIX1	#define FIX2 *--tos = 0#endif#ifdef Mmips	#define FIX1	#define FIX2#endif#ifdef Mpower	/* This could be wrong ... */	#define FIX1	#define FIX2 *--tos = 0#endif#ifdef Malpha	#define FIX1 *--tos = 0	#define FIX2 *--tos = 0#endif#ifdef Marm	#define FIX1	#define FIX2#endif static intnextID(void){	static Lock l;	static id;	int i; 	lock(&l);	i = ++id;	unlock(&l);	return i;}	static intnotehandler(void *, char *s) {	Proc *p;	Thread *t;	int id; 	if (DBGNOTE & _threaddebuglevel)		threadprint(2, "Got note %s\n", s);	if (getpid() == passerpid) {		if (DBGNOTE & _threaddebuglevel)			threadprint(2, "Notepasser %d got note %s\n", passerpid, s);		if (strcmp(s, "kilpasser") == 0)			exits(nil);		write(notefd, s, strlen(s));		if (strncmp(s, "kilthr", 6) == 0 || strncmp(s, "kilgrp", 6) == 0)			return 1;	} else {		if (strncmp(s, "kilthr", 6) == 0) {			p = *procp;			id = strtoul(s+6, nil, 10);			if (DBGNOTE & _threaddebuglevel)				threadprint(2, "Thread id %d\n", id);			for (t = p->threads.head; t; t = t->nextt) {				if (t->id == id) {					break;				}			}			if (t == nil) {				if (DBGNOTE & _threaddebuglevel)					threadprint(2, "Thread id %d not found\n", id);				return 1;			}			if (t == p->curthread) {				char err[ERRLEN] = "exiting";				if (DBGNOTE & _threaddebuglevel)					threadprint(2, "Killing current thread\n");				errstr(err);			}			threadassert(t->state != Rendezvous);			t->exiting = 1;			return 1;		}		if (strncmp(s, "kilgrp", 6) == 0) {			p = *procp;			id = strtoul(s+6, nil, 10);			if (DBGNOTE & _threaddebuglevel)				threadprint(2, "Thread grp %d", id);			for (t = p->threads.head; t; t = t->nextt) {				if (t->grp == id) {					if (t != p->curthread)						t->exiting = 1;				}			}			return 1;		}	}	if (strncmp(s, "kilall", 6) == 0)		exits(s+6);	return 0;} voidthreadexits(char *exitstr) {	Thread *t, *new;	Proc *p;	Channel *c; 	_threaddebug(DBGTHRD|DBGKILL, "Exitthread()");	p = *procp;	t = p->curthread;	threadassert(t->state == Running);	if (p->nthreads > 1) {		t->exiting = 1;		p->nthreads--;		lock(&rendezlock);		while ((new = getq(&p->runnable)) == nil) {			_threaddebug(DBGTHRD, "Nothing left to run");			/* called with rendezlock held */			p->blocked = 1;			unlock(&rendezlock);			if ((new = (Thread *)rendezvous((ulong)p, 0)) != (Thread *)~0) {				threadassert(!p->blocked);				threadassert(new->proc == p);				p->curthread = new;				new->state = Running;				/* switch to new thread, pass it `t' for cleanup */				new->garbage = t;				longjmp(new->env, (int)t);				/* No return */			}			_threaddebug(DBGNOTE|DBGTHRD, "interrupted");			lock(&rendezlock);		}		unlock(&rendezlock);		_threaddebug(DBGTHRD, "Yield atexit to %d.%d", p->pid, new->id);		p->curthread = new;		new->state = Running;		if (new->exiting)			threadexits(nil);		/* switch to new thread, pass it `t' for cleanup */		new->garbage = t;		longjmp(new->env, (int)t);		/* no return */	}	/*	 * thewaitchan confounds exiting the entire program, so handle it	 * carefully; store exposed global in local variable c:	 */	if ((c = thewaitchan) != nil) {		Waitmsg w;		long t[4]; 		snprint(w.pid, sizeof(w.pid), "%d", p->pid);		times(t);		snprint(w.time + 0*12, 12, "%10.2fu", t[0]/1000.0);		snprint(w.time + 1*12, 12, "%10.2fu", t[1]/1000.0);		snprint(w.time + 2*12, 12, "%10.2fu",			(t[0]+t[1]+t[2]+t[3])/1000.0);	/* Cheating just a bit */		if (exitstr)			strncpy(w.msg, exitstr, sizeof(w.msg));		else			w.msg[0] = '\0';		_threaddebug(DBGCHLD, "In thread %s: sending exit status %s for %d\n",			p->curthread->cmdname, exitstr, p->pid);		send(c, &w);	}	_threaddebug(DBGPROC, "Exiting\n");	t->exiting = 1;	if(exitstr == nil)		p->str[0] = '\0';	else		strncpy(p->str, exitstr, sizeof(p->str));	p->nthreads--;	/* Clean up and exit */	longjmp(p->oldenv, DOEXIT);} static Thread *threadof(int id) {	Proc *pp;	Thread *t; 	lock(&pq.lock);	for (pp = pq.head; pp->next; pp = pp->next)		for (t = pp->threads.head; t; t = t->nextt)			if (t->id == id) {				unlock(&pq.lock);				return t;			}	unlock(&pq.lock);	return nil;} intthreadpid(int id) {	Proc *pp;	Thread *t; 	if (id < 0)		return id;	if (id == 0)		return (*procp)->pid;	lock(&pq.lock);	for (pp = pq.head; pp->next; pp = pp->next)		for (t = pp->threads.head; t; t = t->nextt)			if (t->id == id) {				unlock(&pq.lock);				return pp->pid;			}	unlock(&pq.lock);	return -1;} voidthreadkillgrp(int grp) {	int fd;	char buf[128]; 	sprint(buf, "/proc/%d/notepg", getpid());	fd = open(buf, OWRITE|OCEXEC);	sprint(buf, "kilgrp%d", grp);	write(fd, buf, strlen(buf));	close(fd);} voidthreadkill(int id) {	char buf[16];	Thread *t;	int pid;	Proc *p; 	if ((t = threadof(id)) == nil) {		if (_threaddebuglevel & DBGNOTE)			threadprint(2, "Can't find thread to kill\n");		return;	}	sprint(buf, "kilthr%d", id);	if ((pid = t->proc->pid) == 0) {		if (_threaddebuglevel & DBGNOTE)			threadprint(2, "Thread has zero pid\n");		if (postnote(PNGROUP, getpid(), buf) < 0)			threadprint(2, "postnote failed: %r\n");		return;	}	p = *procp;	if (pid == p->pid) {		if (_threaddebuglevel & DBGNOTE)			threadprint(2, "Thread in same proc\n");		assert(pid == getpid());		if (t == p->curthread) {			threadprint(2, "Threadkill:  killing self\n");			threadexits("threadicide");		}		lock(&rendezlock);		if (t->state == Rendezvous) {			if (_threaddebuglevel & DBGNOTE)				threadprint(2, "Thread was in rendezvous\n");			assert(getqbytag(&rendez, t->tag) == t);		}		t->exiting = 1;		unlock(&rendezlock);		garbagethread(t);		return;	}	if (postnote(PNPROC, pid, buf) < 0)		threadprint(2, "postnote failed: %r\n");} voidthreadexitsall(char *status) {	int fd;	Channel *c;	char buf[128]; 	if ((c = thewaitchan) != nil) {		thewaitchan = nil;		while (nbrecv(c, nil))			;	/* Unblock threads previously exited,				 * sending on thewaitchan, not yet waited for				 */	}	sprint(buf, "/proc/%d/note", passerpid);	if ((fd = open(buf, OWRITE)) < 0) {		/* passer proc died, send note to notepg directly */		sprint(buf, "/proc/%d/notepg", getpid());		if ((fd = open(buf, OWRITE)) < 0) {			threadprint(2, "threadexitsall: can't open %s: %r\n", buf);			exits("panic");		}		snprint(buf, sizeof buf, "kilall%s", status?status:"");		if (DBGNOTE & _threaddebuglevel)			fprint(2, "Sending note %s to process group of %d\n", buf, getpid());	} else {		snprint(buf, sizeof buf, "kilall%s", status?status:"");		if (DBGNOTE & _threaddebuglevel)			fprint(2, "Sending note %s to passer proc %d\n", buf, passerpid);	}	write(fd, buf, strlen(buf));	exits(status);} voidyield(void) {	Thread *new, *t;	Proc *p;	ulong thr; 	p = *procp;	t = p->curthread; 	if (t->exiting) {		_threaddebug(DBGTHRD|DBGKILL, "Exiting in yield()");		threadexits(nil);	/* no return */	}	if ((new = getq(&p->runnable)) == nil) {		_threaddebug(DBGTHRD, "Nothing to yield for");		return;	/* Nothing to yield for */	}	if ((thr = setjmp(p->curthread->env))) {		if (thr != ~0)			garbagethread((Thread *)thr);		if ((*procp)->curthread->exiting)			threadexits(nil);		return;	/* Return from yielding */	}	putq(&p->runnable, p->curthread);	p->curthread->state = Runnable;	_threaddebug(DBGTHRD, "Yielding to %d.%d", p->pid, new->id);	p->curthread = new;	new->state = Running;	longjmp(new->env, ~0);	/* no return */} ulong_threadrendezvous(ulong tag, ulong value) {	Proc *p;	Thread *this, *that, *new;	ulong v, t; 	p = *procp;	this = p->curthread; 	lock(&rendezlock);	_threaddebug(DBGREND, "rendezvous tag %lux", tag);	/* find a thread waiting in a rendezvous on tag */	that = getqbytag(&rendez, tag);	/* if a thread in same or another proc waiting, rendezvous */	if (that) {		_threaddebug(DBGREND, "waiting proc is %lud.%d", that->proc->pid, that->id);		threadassert(that->state == Rendezvous);		/* exchange values */		v = that->value;		that->value = value;		/* remove from rendez-vous queue */		that->state = Runnable;		if (that->proc->blocked) {			threadassert(that->proc != p);			that->proc->blocked = 0;			_threaddebug(DBGREND, "unblocking rendezvous, tag = %lux", (ulong)that->proc);			unlock(&rendezlock);			/* `Non-blocking' rendez-vous */			while (rendezvous((ulong)that->proc, (ulong)that) == ~0) {				_threaddebug(DBGNOTE|DBGTHRD, "interrupted");				if (this->exiting) {					_threaddebug(DBGNOTE|DBGTHRD, "and committing suicide");					threadexits(nil);				}			}		} else {			putq(&that->proc->runnable, that);			unlock(&rendezlock);		}		yield();		return v;	}	_threaddebug(DBGREND, "blocking");	/* Mark this thread waiting */	this->value = value;	this->state = Rendezvous;	this->tag = tag;	putq(&rendez, this); 	/* Look for runnable threads */	new = getq(&p->runnable);	if (new == nil) {		/* No other thread runnable, rendezvous */		p->blocked = 1;		_threaddebug(DBGREND, "blocking rendezvous, tag = %lux", (ulong)p);		unlock(&rendezlock);		while ((new = (Thread *)rendezvous((ulong)p, 0)) == (Thread *)~0) {			_threaddebug(DBGNOTE|DBGTHRD, "interrupted");			if (this->exiting) {				_threaddebug(DBGNOTE|DBGTHRD, "and committing suicide");				threadexits(nil);			}		}		threadassert(!p->blocked);		threadassert(new->proc == p);		if (new == this) {			this->state = Running;			if (this->exiting)				threadexits(nil);			return this->value;		}		_threaddebug(DBGREND, "after rendezvous, new thread is %lux, id %d", new, new->id);	} else {		unlock(&rendezlock);	}	if ((t = setjmp(p->curthread->env))) {		if (t != ~0)			garbagethread((Thread *)t);		_threaddebug(DBGREND, "unblocking");		threadassert(p->curthread->next == (Thread *)~0);		this->state = Running;		if (p->curthread->exiting) {			_threaddebug(DBGKILL, "Exiting after rendezvous");			threadexits(nil);		}		return this->value;	}	_threaddebug(DBGREND|DBGTHRD, "Scheduling %lud.%d", new->proc->pid, new->id);	p->curthread = new;	new->state = Running;	longjmp(new->env, ~0);	/* no return */	return ~0;	/* Not called */} voidmain(int argc, char *argv[]){	char buf[40], err[ERRLEN];	int fd, id; 	_qlockinit(_threadrendezvous);	id = getnoteid();	threadassert(id > 0);	rfork(RFNOTEG|RFREND);	atnotify(notehandler, 1);	switch(rfork(RFPROC|RFMEM)){	case -1:		threadassert(0);	case 0:		/* handle notes */		rfork(RFCFDG);		if (_threaddebuglevel) {			fd = open("/dev/cons", OWRITE);			dup(fd, 2);		}		passerpid = getpid();		/*		 * The noteid is attached to the fd at open.		 * Notefd will correspond to our current (new) noteid		 * even after we set our noteid back to the old one.		 *		 * We could open main's notepg file and not depend		 * on this behavior, but then we would depend on the fact that		 * when main exits its open notepg file is still writable.		 * We would also depend on opening the file before main		 * exits, which is a race I'd rather not run.		 */		sprint(buf, "/proc/%d/notepg", getpid());		notefd = open(buf, OWRITE|OCEXEC);		if (_threaddebuglevel & DBGNOTE)			threadprint(2, "Passer is %d\n", passerpid);		if(setnoteid(id) < 0){			passerpid = -1;			exits(0);		}		for(;;){			if(sleep(120*1000) < 0){

Lines 1–500 of 1157.