Continue fighting pthreads.
Clean up thread library a bit too.
diff --git a/src/libthread/sched.c b/src/libthread/sched.c
index 7f7b6da..3fb2ff2 100644
--- a/src/libthread/sched.c
+++ b/src/libthread/sched.c
@@ -1,76 +1,59 @@
-#include <u.h>
-#include <signal.h>
-#include <errno.h>
+/*
+ * Thread scheduler.
+ */
 #include "threadimpl.h"
 
-static Thread	*runthread(Proc*);
+static Thread *runthread(Proc*);
+static void schedexit(Proc*);
 
-static char *_psstate[] = {
-	"Dead",
-	"Running",
-	"Ready",
-	"Rendezvous",
-};
-
-static char*
-psstate(int s)
-{
-	if(s < 0 || s >= nelem(_psstate))
-		return "unknown";
-	return _psstate[s];
-}
-
+/*
+ * Main scheduling loop.
+ */
 void
-needstack(int howmuch)
-{
-	Proc *p;
-	Thread *t;
-
-	p = _threadgetproc();
-	if(p == nil || (t=p->thread) == nil)
-		return;
-	if((ulong)&howmuch < (ulong)t->stk+howmuch){	/* stack overflow waiting to happen */
-		fprint(2, "stack overflow: stack at 0x%lux, limit at 0x%lux, need 0x%lux\n", (ulong)&p, (ulong)t->stk, howmuch);
-		abort();
-	}
-}
-
-void
-_scheduler(void *arg)
+_threadscheduler(void *arg)
 {
 	Proc *p;
 	Thread *t;
 
 	p = arg;
-	lock(&p->lock);
-	p->pid = _threadgetpid();
-	_threadsetproc(p);
+
+	_threadlinkmain();
+	_threadinitproc(p);
 
 	for(;;){
+		/* 
+		 * Clean up zombie children.
+		 */
+		_threadwaitkids(p);
+
+		/*
+		 * Find next thread to run.
+		 */
+		_threaddebug(DBGSCHED, "runthread");
 		t = runthread(p);
-		if(t == nil){
-			_threaddebug(DBGSCHED, "all threads gone; exiting");
-			_threaddelproc();
-			_schedexit(p);
-		}
-		_threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id);
-		p->thread = t;
-		if(t->moribund){
-			_threaddebug(DBGSCHED, "%d.%d marked to die");
-			goto Moribund;
-		}
-		t->state = Running;
-		t->nextstate = Ready;
-		unlock(&p->lock);
-
-		_swaplabel(&p->sched, &t->sched);
-
+		if(t == nil)
+			schedexit(p);
+	
+		/*
+		 * If it's ready, run it (might instead be marked to die).
+		 */
 		lock(&p->lock);
-		p->thread = nil;
+		if(t->state == Ready){
+			_threaddebug(DBGSCHED, "running %d.%d", p->id, t->id);
+			t->state = Running;
+			t->nextstate = Ready;
+			p->thread = t;
+			unlock(&p->lock);
+			_swaplabel(&p->context, &t->context);
+			lock(&p->lock);
+			p->thread = nil;
+		}
+
+		/*
+		 * If thread needs to die, kill it.
+		 */
 		if(t->moribund){
-		Moribund:
-			if(t->moribund != 1)
-				fprint(2, "moribund %d\n", t->moribund);
+			_threaddebug(DBGSCHED, "moribund %d.%d", p->id, t->id);
 			assert(t->moribund == 1);
 			t->state = Dead;
 			if(t->prevt)
@@ -82,40 +65,37 @@
 			else
 				p->threads.tail = t->prevt;
 			unlock(&p->lock);
-			if(t->inrendez){
-				abort();
-			//	_threadflagrendez(t);
-			//	_threadbreakrendez();
-			}
-			_stackfree(t->stk);
-			free(t->cmdname);
-			free(t);	/* XXX how do we know there are no references? */
+			_threadfree(t);
 			p->nthreads--;
 			t = nil;
-			lock(&p->lock);
 			continue;
 		}
-/*
-		if(p->needexec){
-			t->ret = _schedexec(&p->exec);
-			p->needexec = 0;
+		unlock(&p->lock);
+
+		/*
+		 * If there is a request to run a function on the 
+		 * scheduling stack, do so.
+		 */
+		if(p->schedfn){
+			_threaddebug(DBGSCHED, "schedfn");
+			p->schedfn(p);
+			p->schedfn = nil;
+			_threaddebug(DBGSCHED, "schedfn ended");
 		}
-*/
-		if(p->newproc){
-			t->ret = _schedfork(p->newproc);
-			if(t->ret < 0){
-//fprint(2, "_schedfork: %r\n");
-				abort();
-			}
-			p->newproc = nil;
-		}
+
+		/*
+		 * Move the thread along.
+		 */
 		t->state = t->nextstate;
+		_threaddebug(DBGSCHED, "moveon %d.%d", p->id, t->id);
 		if(t->state == Ready)
 			_threadready(t);
-		unlock(&p->lock);
 	}
 }
 
+/*
+ * Called by thread to give up control of processor to scheduler.
+ */
 int
 _sched(void)
 {
@@ -125,146 +105,14 @@
 	p = _threadgetproc();
 	t = p->thread;
 	assert(t != nil);
-	_swaplabel(&t->sched, &p->sched);
+	_swaplabel(&t->context, &p->context);
 	return p->nsched++;
 }
 
-static Thread*
-runthread(Proc *p)
-{
-	Channel *c;
-	Thread *t;
-	Tqueue *q;
-	Waitmsg *w;
-	int e, sent;
-
-	if(p->nthreads==0 || (p->nthreads==1 && p->idle))
-		return nil;
-	q = &p->ready;
-relock:
-	lock(&p->readylock);
-	if(p->nsched%128 == 0){
-		/* clean up children */
-		e = errno;
-		if((c = _threadwaitchan) != nil){
-			if(c->n <= c->s){
-				sent = 0;
-				for(;;){
-					if((w = p->waitmsg) != nil)
-						p->waitmsg = nil;
-					else
-						w = waitnohang();
-					if(w == nil)
-						break;
-					if(sent == 0){
-						unlock(&p->readylock);
-						sent = 1;
-					}
-					if(nbsendp(c, w) != 1)
-						break;
-				}
-				p->waitmsg = w;
-				if(sent)
-					goto relock;
-			}
-		}else{
-			while((w = waitnohang()) != nil)
-				free(w);
-		}
-		errno = e;
-	}
-	if(q->head == nil){
-		if(p->idle){
-			if(p->idle->state != Ready){
-				fprint(2, "everyone is asleep\n");
-				exits("everyone is asleep");
-			}
-			unlock(&p->readylock);
-			_threaddebug(DBGSCHED, "running idle thread", p->nthreads);
-			return p->idle;
-		}
-
-		_threaddebug(DBGSCHED, "sleeping for more work (%d threads)", p->nthreads);
-		q->asleep = 1;
-		p->rend.l = &p->readylock;
-		_procsleep(&p->rend);
-		if(_threadexitsallstatus)
-			_exits(_threadexitsallstatus);
-	}
-	t = q->head;
-	q->head = t->next;
-	unlock(&p->readylock);
-	return t;
-}
-
-long
-threadstack(void)
-{
-	Proc *p;
-	Thread *t;
-
-	p = _threadgetproc();
-	t = p->thread;
-	return (ulong)&p - (ulong)t->stk;
-}
-
-void
-_threadready(Thread *t)
-{
-	Tqueue *q;
-
-	if(t == t->proc->idle){
-		_threaddebug(DBGSCHED, "idle thread is ready");
-		return;
-	}
-
-	assert(t->state == Ready);
-	_threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id);
-	q = &t->proc->ready;
-	lock(&t->proc->readylock);
-	t->next = nil;
-	if(q->head==nil)
-		q->head = t;
-	else
-		q->tail->next = t;
-	q->tail = t;
-	if(q->asleep){
-		assert(q->asleep == 1);
-		q->asleep = 0;
-		/* lock passes to runthread */
-		_procwakeup(&t->proc->rend);
-	}
-	unlock(&t->proc->readylock);
-	if(_threadexitsallstatus)
-		_exits(_threadexitsallstatus);
-}
-
-void
-_threadidle(void)
-{
-	Tqueue *q;
-	Thread *t, *idle;
-	Proc *p;
-
-	p = _threadgetproc();
-	q = &p->ready;
-	lock(&p->readylock);
-	assert(q->tail);
-	idle = q->tail;
-	if(q->head == idle){
-		q->head = nil;
-		q->tail = nil;
-	}else{
-		for(t=q->head; t->next!=q->tail; t=t->next)
-			;
-		t->next = nil;
-		q->tail = t;
-	}
-	p->idle = idle;
-	_threaddebug(DBGSCHED, "p->idle is %d\n", idle->id);
-	unlock(&p->readylock);
-}
-
+/*
+ * Called by thread to yield the processor to other threads.
+ * Returns number of other threads run between call and return.
+ */
 int
 yield(void)
 {
@@ -276,15 +124,176 @@
 	return _sched() - nsched;
 }
 
-void
-threadstatus(void)
+/*
+ * Choose the next thread to run.
+ */
+static Thread*
+runthread(Proc *p)
 {
-	Proc *p;
 	Thread *t;
+	Tqueue *q;
+
+	/*
+	 * No threads left?
+	 */
+	if(p->nthreads==0 || (p->nthreads==1 && p->idle))
+		return nil;
+
+	lock(&p->readylock);
+	q = &p->ready;
+	if(q->head == nil){
+		/*
+		 * Is this a single-process program with an idle thread?
+		 */
+		if(p->idle){
+			/*
+			 * The idle thread had better be ready!
+			 */
+			if(p->idle->state != Ready)
+				sysfatal("all threads are asleep");
+
+			/*
+			 * Run the idle thread.
+			 */
+			unlock(&p->readylock);
+			_threaddebug(DBGSCHED, "running idle thread", p->nthreads);
+			return p->idle;
+		}
+
+		/*
+		 * Wait until one of our threads is readied (by another proc!).
+		 */
+		q->asleep = 1;
+		p->rend.l = &p->readylock;
+		_procsleep(&p->rend);
+
+		/*
+		 * Maybe we were awakened to exit?
+		 */
+		if(_threadexitsallstatus)
+			_exits(_threadexitsallstatus);
+
+		assert(q->head != nil);
+	}
+
+	t = q->head;
+	q->head = t->next;
+	unlock(&p->readylock);
+
+	return t;
+}
+
+/*
+ * Add a newly-ready thread to its proc's run queue.
+ */
+void
+_threadready(Thread *t)
+{
+	Tqueue *q;
+
+	/*
+	 * The idle thread does not go on the run queue.
+	 */
+	if(t == t->proc->idle){
+		_threaddebug(DBGSCHED, "idle thread is ready");
+		return;
+	}
+
+	assert(t->state == Ready);
+	_threaddebug(DBGSCHED, "readying %d.%d", t->proc->id, t->id);
+
+	/*
+	 * Add thread to run queue.
+	 */
+	q = &t->proc->ready;
+	lock(&t->proc->readylock);
+
+	t->next = nil;
+	if(q->head == nil)
+		q->head = t;
+	else
+		q->tail->next = t;
+	q->tail = t;
+
+	/*
+	 * Wake proc scheduler if it is sleeping.
+	 */
+	if(q->asleep){
+		assert(q->asleep == 1);
+		q->asleep = 0;
+		_procwakeup(&t->proc->rend);
+	}
+	unlock(&t->proc->readylock);
+}
+
+/*
+ * Mark the given thread as the idle thread.
+ * Since the idle thread was just created, it is sitting
+ * somewhere on the ready queue.
+ */
+void
+_threadsetidle(int id)
+{
+	Tqueue *q;
+	Thread *t, **l, *last;
+	Proc *p;
 
 	p = _threadgetproc();
-	for(t=p->threads.head; t; t=t->nextt)
-		fprint(2, "[%3d] %s userpc=%lux\n",
-			t->id, psstate(t->state), t->userpc);
+
+	lock(&p->readylock);
+
+	/*
+	 * Find thread on ready queue.
+	 */
+	q = &p->ready;
+	for(l=&q->head, last=nil; (t=*l) != nil; l=&t->next, last=t)
+		if(t->id == id)
+			break;
+	assert(t != nil);
+
+	/* 
+	 * Remove it from ready queue.
+	 */
+	*l = t->next;
+	if(t == q->head)
+		q->head = t->next;
+	if(t->next == nil)
+		q->tail = last;
+
+	/*
+	 * Set as idle thread.
+	 */
+	p->idle = t;
+	_threaddebug(DBGSCHED, "p->idle is %d\n", t->id);
+	unlock(&p->readylock);
 }
-	
+
+static void
+schedexit(Proc *p)
+{
+	char ex[ERRMAX];
+	int n;
+	Proc **l;
+
+	_threaddebug(DBGSCHED, "exiting proc %d", p->id);
+	lock(&_threadpq.lock);
+	for(l=&_threadpq.head; *l; l=&(*l)->next){
+		if(*l == p){
+			*l = p->next;
+			if(*l == nil)
+				_threadpq.tail = l;
+			break;
+		}
+	}
+	n = --_threadnprocs;
+	unlock(&_threadpq.lock);
+
+	strncpy(ex, p->exitstr, sizeof ex);
+	ex[sizeof ex-1] = '\0';
+	free(p);
+	if(n == 0)
+		_threadexitallproc(ex);
+	else
+		_threadexitproc(ex);
+}
+