Commit Diff


commit - 3d5e34e146b5ba5c973230abb624ce9126024569
commit + c6687d4591964cb13df87f55ec4770e778a4a55c
blob - 87b6bfd038e5d7bb3d761b6a33f34f5174609f08
blob + da9f9dc1e36ad5c6ff30aef3da6aac0492df6734
--- include/libc.h
+++ include/libc.h
@@ -436,6 +436,7 @@ struct Lock
 #endif
 };
 
+extern	int	_tas(int*);
 extern	void	lock(Lock*);
 extern	void	unlock(Lock*);
 extern	int	canlock(Lock*);
blob - 8e79ba6e1ca97bbfd8dd8b3e32740f837b48108d
blob + c1ca03365434d6d0659698101d25416c9c25feaf
--- include/u.h
+++ include/u.h
@@ -24,7 +24,6 @@ extern "C" {
 #include <fmt.h>
 #include <math.h>
 #include <ctype.h>	/* for tolower */
-#include <pthread.h>	/* for Locks */
 
 /*
  * OS-specific crap
@@ -42,9 +41,11 @@ typedef long p9jmp_buf[sizeof(sigjmp_buf)/sizeof(long)
 #		undef _NEEDUSHORT
 #		undef _NEEDUINT
 #		undef _NEEDULONG
-#	include <pthread.h>
-#	define PLAN9_PTHREADS
 #	endif
+#	if defined(__Linux26__)
+#		include <pthread.h>
+#		define PLAN9_PTHREADS 1
+#	endif
 #endif
 #if defined(__sun__)
 #	include <sys/types.h>
blob - 51e733ab720f6b8825b0fcfbc399cc9ed5ff17b1
blob + f183f115c48e39f348fc22a487f8dbee9473946e
--- src/cmd/9term/9term.c
+++ src/cmd/9term/9term.c
@@ -884,12 +884,8 @@ key(Rune r)
 	case 0x7F:	/* DEL: send interrupt */
 		t.qh = t.q0 = t.q1 = t.nr;
 		show(t.q0);
-{int x; x=tcgetpgrp(rcfd);
-print("postnote %d pgrp %d\n", rcpid, x);
-		postnote(PNGROUP, x, "interrupt");
-if(x >= 2) killpg(x, 2);
-}
-	//	write(rcfd, "\x7F", 1);
+	//	postnote(PNGROUP, x, "interrupt");
+		write(rcfd, "\x7F", 1);
 		return;
 	}
 
blob - 7ee49350462bb15fbe5820e63e60a90269a015f0
blob + ddaadb56c646c2a907ca007b195dc20c1d48a7cc
--- src/cmd/9term/rcstart.c
+++ src/cmd/9term/rcstart.c
@@ -3,6 +3,25 @@
 #include <libc.h>
 #include "term.h"
 
+static void
+sys(char *buf)
+{
+	char buf2[100];
+	char *f[20];
+	int nf, pid;
+
+	strcpy(buf2, buf);
+	nf = tokenize(buf2, f, nelem(f));
+	f[nf] = nil;
+	switch(pid = fork()){
+	case 0:
+		execvp(f[0], f);
+		_exits("oops");
+	default:
+		waitpid();
+	}
+}
+
 int
 rcstart(int argc, char **argv, int *pfd, int *tfd)
 {
@@ -33,11 +52,14 @@ rcstart(int argc, char **argv, int *pfd, int *tfd)
 		dup(sfd, 0);
 		dup(sfd, 1);
 		dup(sfd, 2);
-		system("stty tabs -onlcr onocr icanon echo erase '^h' intr '^?'");
+		sys("stty tabs -onlcr onocr icanon echo erase '^h' intr '^?'");
 		if(noecho)
-			system("stty -echo");
+			sys("stty -echo");
 		for(i=3; i<100; i++)
 			close(i);
+		signal(SIGINT, SIG_DFL);
+		signal(SIGHUP, SIG_DFL);
+		signal(SIGTERM, SIG_DFL);
 		execvp(argv[0], argv);
 		fprint(2, "exec %s failed: %r\n", argv[0]);
 		_exits("oops");
blob - 0eb5f410edfbdc964b0a5aa390f28baf387c55f5
blob + 0c359c9e072aa471bbd08145df523387666ce190
--- src/lib9/9proc.h
+++ src/lib9/9proc.h
@@ -13,9 +13,6 @@ struct Uproc
 	int pid;
 	int state;
 	void *priv[NPRIV];
-	ulong rendval;
-	ulong rendtag;
-	Uproc *rendhash;
 	p9jmp_buf notejb;
 };
 
blob - /dev/null
blob + 902a957908f1c6c20f3d2dcd80d22952b1677d7a (mode 644)
--- /dev/null
+++ src/lib9/_p9proc-Linux.c
@@ -0,0 +1,5 @@
+#ifdef __Linux26__
+#include "_p9proc-pthread.c"
+#else
+#include "_p9proc-getpid.c"
+#endif
blob - /dev/null
blob + 9543bf2407c586755e85d19e9a8ae300e789d8ad (mode 644)
--- /dev/null
+++ src/lib9/_p9proc-getpid.c
@@ -0,0 +1,113 @@
+/*
+ * This needs to be callable from a signal handler, so it has been
+ * written to avoid locks.  The only lock is the one used to acquire
+ * an entry in the table, and we make sure that acquiring is done
+ * when not in a handler.  Lookup and delete do not need locks.
+ * It's a scan-forward hash table.  To avoid breaking chains, 
+ * T ((void*)-1) is used as a non-breaking nil.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "9proc.h"
+
+enum { PIDHASH = 1021 };
+
+#define T ((void*)-1)
+static Uproc *alluproc[PIDHASH];
+static int allupid[PIDHASH];
+static Lock uproclock;
+
+void
+_clearuproc(void)
+{
+	int i;
+
+	/* called right after fork - no locking needed */
+	for(i=0; i<PIDHASH; i++)
+		if(alluproc[i] != T && alluproc[i] != 0)
+			free(alluproc[i]);
+	memset(alluproc, 0, sizeof alluproc);
+	memset(allupid, 0, sizeof allupid);
+}
+		
+Uproc*
+_p9uproc(int inhandler)
+{
+	int i, h, pid;
+	Uproc *up;
+
+	/* for now, assume getpid is fast or cached */
+	pid = getpid();
+
+	/*
+	 * this part - the lookup - needs to run without locks
+	 * so that it can safely be called from within the notify handler.
+	 * notify calls _p9uproc, and fork and rfork call _p9uproc
+	 * in both parent and child, so if we're in a signal handler,
+	 * we should find something in the table.
+	 */
+	h = pid%PIDHASH;
+	for(i=0; i<PIDHASH; i++){
+		up = alluproc[h];
+		if(up == nil)
+			break;
+		if(allupid[h] == pid)
+			return up;
+		if(++h == PIDHASH)
+			h = 0;
+	}
+
+	if(inhandler){
+		fprint(2, "%s: did not find uproc for pid %d in signal handler\n", argv0, pid);
+		abort();	
+	}
+
+	/* need to allocate */
+	while((up = mallocz(sizeof(Uproc), 1)) == nil)
+		sleep(1000);
+
+	/* fprint(2, "alloc uproc for pid %d\n", pid); */
+	up->pid = pid;
+	lock(&uproclock);
+	h = pid%PIDHASH;
+	for(i=0; i<PIDHASH; i++){
+		if(alluproc[h]==T || alluproc[h]==nil){
+			alluproc[h] = up;
+			allupid[h] = pid;
+			unlock(&uproclock);
+			return up;
+		}
+		if(++h == PIDHASH)
+			h = 0;
+	}
+	unlock(&uproclock);
+
+	/* out of pids! */
+	sysfatal("too many processes in uproc table");
+	return nil;
+}
+
+void
+_p9uprocdie(void)
+{
+	Uproc *up;
+	int pid, i, h;
+
+	pid = getpid();
+	/* fprint(2, "reap uproc for pid %d\n", pid); */
+	h = pid%PIDHASH;
+	for(i=0; i<PIDHASH; i++){
+		up = alluproc[h];
+		if(up == nil)
+			break;
+		if(up == T)
+			continue;
+		if(allupid[h] == pid){
+			up = alluproc[h];
+			alluproc[h] = T;
+			free(up);
+			allupid[h] = 0;
+		}
+	}
+}
blob - 4dbff87b014d7ab5136898bcdcf281dbf83a2d0c
blob + 841d2c2d7a18711b1bfc6c71446975b3e252f4dd
--- src/lib9/fork.c
+++ src/lib9/fork.c
@@ -1,4 +1,5 @@
 #include <u.h>
+#include <signal.h>
 #include <libc.h>
 #include "9proc.h"
 #undef fork
@@ -7,9 +8,15 @@ int
 p9fork(void)
 {
 	int pid;
+	sigset_t all, old;
 
+	sigfillset(&all);
+	sigprocmask(SIG_SETMASK, &all, &old);
 	pid = fork();
-	_clearuproc();
-	_p9uproc(0);
+	if(pid == 0){
+		_clearuproc();
+		_p9uproc(0);
+	}
+	sigprocmask(SIG_SETMASK, &old, nil);
 	return pid;
 }
blob - /dev/null
blob + 231ca713ceb6147b8691d80b1b0d2b2436c7448e (mode 644)
--- /dev/null
+++ src/lib9/lock-Darwin.c
@@ -0,0 +1 @@
+#include "lock-pthread.c"
blob - /dev/null
blob + bdd48a28e7e3b49884456f46f8f81c35c6fbf297 (mode 644)
--- /dev/null
+++ src/lib9/lock-FreeBSD.c
@@ -0,0 +1 @@
+#include "lock-tas.c"
blob - /dev/null
blob + c25596b9aeadfbc323fe2cc48494904dff962a7c (mode 644)
--- /dev/null
+++ src/lib9/lock-Linux.c
@@ -0,0 +1,5 @@
+#ifdef __Linux26__
+#include "lock-pthread.c"
+#else
+#include "lock-tas.c"
+#endif
blob - /dev/null
blob + 689261f6378678b2321552254bc74102b2966344 (mode 644)
--- /dev/null
+++ src/lib9/lock-pthread.c
@@ -0,0 +1,54 @@
+#include <u.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <libc.h>
+
+static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+lockinit(Lock *lk)
+{
+	pthread_mutexattr_t attr;
+
+	pthread_mutex_lock(&initmutex);
+	if(lk->init == 0){
+		pthread_mutexattr_init(&attr);
+		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+		pthread_mutex_init(&lk->mutex, &attr);
+		pthread_mutexattr_destroy(&attr);
+		lk->init = 1;
+	}
+	pthread_mutex_unlock(&initmutex);
+}
+
+void
+lock(Lock *lk)
+{
+	if(!lk->init)
+		lockinit(lk);
+	if(pthread_mutex_lock(&lk->mutex) != 0)
+		abort();
+}
+
+int
+canlock(Lock *lk)
+{
+	int r;
+
+	if(!lk->init)
+		lockinit(lk);
+	r = pthread_mutex_trylock(&lk->mutex);
+	if(r == 0)
+		return 1;
+	if(r == EBUSY)
+		return 0;
+	abort();
+}
+
+void
+unlock(Lock *lk)
+{
+	if(pthread_mutex_unlock(&lk->mutex) != 0)
+		abort();
+}
blob - ed430ff8675efb8d8d4cd61c9bf9651513da8e69
blob + 9200d81cc76f80774cf6ec110c5fb2dd130da688
--- src/lib9/mkfile
+++ src/lib9/mkfile
@@ -67,7 +67,7 @@ LIB9OFILES=\
 	_exits.$O\
 	_p9dialparse.$O\
 	_p9dir.$O\
-	_p9proc.$O\
+	_p9proc-$SYSNAME.$O\
 	announce.$O\
 	argv0.$O\
 	atexit.$O\
@@ -111,7 +111,7 @@ LIB9OFILES=\
 	jmp.$O\
 	lrand.$O\
 	lnrand.$O\
-	lock.$O\
+	lock-$SYSNAME.$O\
 	main.$O\
 	malloc.$O\
 	malloctag.$O\
@@ -141,6 +141,7 @@ LIB9OFILES=\
 	strecpy.$O\
 	sysfatal.$O\
 	sysname.$O\
+	tas-$OBJTYPE.$O\
 	time.$O\
 	tokenize.$O\
 	truerand.$O\
blob - /dev/null
blob + e6f54de65c0fe4e6a4f5883d77db1ea1ee7616a0 (mode 644)
--- /dev/null
+++ src/lib9/lock-tas.c
@@ -0,0 +1,57 @@
+#include <u.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <libc.h>
+
+int _ntas;
+static int
+_xtas(void *v)
+{
+	int x;
+
+	_ntas++;
+	x = _tas(v);
+	if(x != 0 && x != 0xcafebabe){
+		print("bad tas value %d\n", x);
+		abort();
+	}
+	return x;
+}
+
+int
+canlock(Lock *l)
+{
+	return !_xtas(&l->val);
+}
+
+void
+unlock(Lock *l)
+{
+	l->val = 0;
+}
+
+void
+lock(Lock *lk)
+{
+	int i;
+
+	/* once fast */
+	if(!_xtas(&lk->val))
+		return;
+	/* a thousand times pretty fast */
+	for(i=0; i<1000; i++){
+		if(!_xtas(&lk->val))
+			return;
+		sched_yield();
+	}
+	/* now nice and slow */
+	for(i=0; i<1000; i++){
+		if(!_xtas(&lk->val))
+			return;
+		usleep(100*1000);
+	}
+	/* take your time */
+	while(_xtas(&lk->val))
+		usleep(1000*1000);
+}
blob - 625c100f5568f2a845d19027c1594158840687db
blob + 0eacccd78d6eaf00d9eceeda90fe96f60151a727
--- src/lib9/qlock.c
+++ src/lib9/qlock.c
@@ -18,6 +18,8 @@ enum
 
 static void (*procsleep)(_Procrend*) = _procsleep;
 static void (*procwakeup)(_Procrend*) = _procwakeup;
+#define _procsleep donotcall_procsleep
+#define _procwakeup donotcall_procwakeup
 
 /* this gets called by the thread library ONLY to get us to use its rendezvous */
 void
@@ -73,7 +75,7 @@ qlock(QLock *q)
 	q->tail = mp;
 	mp->state = Queuing;
 	mp->rend.l = &q->lock;
-	_procsleep(&mp->rend);
+	procsleep(&mp->rend);
 	unlock(&q->lock);
 	assert(mp->state == Waking);
 	unlock(&mp->inuse);
@@ -92,7 +94,7 @@ qunlock(QLock *q)
 		if(q->head == nil)
 			q->tail = nil;
 		p->state = Waking;
-		_procwakeup(&p->rend);
+		procwakeup(&p->rend);
 		unlock(&q->lock);
 		return;
 	}
@@ -137,7 +139,7 @@ rlock(RWLock *q)
 	mp->next = nil;
 	mp->state = QueuingR;
 	mp->rend.l = &q->lock;
-	_procsleep(&mp->rend);
+	procsleep(&mp->rend);
 	unlock(&q->lock);
 	assert(mp->state == Waking);
 	unlock(&mp->inuse);
@@ -181,7 +183,7 @@ runlock(RWLock *q)
 
 	/* wakeup waiter */
 	p->state = Waking;
-	_procwakeup(&p->rend);
+	procwakeup(&p->rend);
 	unlock(&q->lock);
 }
 
@@ -211,7 +213,7 @@ wlock(RWLock *q)
 
 	/* wait in kernel */
 	mp->rend.l = &q->lock;
-	_procsleep(&mp->rend);
+	procsleep(&mp->rend);
 	unlock(&q->lock);
 	assert(mp->state == Waking);
 	unlock(&mp->inuse);
@@ -253,7 +255,7 @@ wunlock(RWLock *q)
 		if(q->head == nil)
 			q->tail = nil;
 		p->state = Waking;
-		_procwakeup(&p->rend);
+		procwakeup(&p->rend);
 		unlock(&q->lock);
 		return;
 	}
@@ -269,7 +271,7 @@ wunlock(RWLock *q)
 		q->head = p->next;
 		q->readers++;
 		p->state = Waking;
-		_procwakeup(&p->rend);
+		procwakeup(&p->rend);
 	}
 	if(q->head == nil)
 		q->tail = nil;
@@ -310,20 +312,20 @@ rsleep(Rendez *r)
 		if(r->l->head == nil)
 			r->l->tail = nil;
 		t->state = Waking;
-		_procwakeup(&t->rend);
+		procwakeup(&t->rend);
 	}else
 		r->l->locked = 0;
 
 	/* wait for a wakeup */
 	me->rend.l = &r->l->lock;
-	_procsleep(&me->rend);
-
+	procsleep(&me->rend);
 	assert(me->state == Waking);
 	unlock(&me->inuse);
 	if(!r->l->locked){
 		fprint(2, "rsleep: not locked after wakeup\n");
 		abort();
 	}
+	unlock(&r->l->lock);
 }
 
 int
blob - e2abc3d35b70c0c394958639335e8bb058d53553
blob + 744a8fc81a5a5f91eb3df0c99c3d3d3051059176
--- src/lib9/rendez-signal.c
+++ src/lib9/rendez-signal.c
@@ -52,6 +52,7 @@ _procsleep(_Procrend *r)
 	 */
 	ignusr1(1);
 	assert(r->asleep == 0);
+	lock(r->l);
 }
 
 void
blob - 2d5074ee1fc11046fffcef403f7d5e4dfb9ba495
blob + aa92c2006454034e85b174f5ac99d739af087c84
--- src/libmux/mux.c
+++ src/libmux/mux.c
@@ -55,8 +55,9 @@ muxrpc(Mux *mux, void *tx)
 	enqueue(mux, r);
 
 	/* wait for our packet */
-	while(mux->muxer && !r->p)
+	while(mux->muxer && !r->p){
 		rsleep(&r->r);
+	}
 
 	/* if not done, there's no muxer: start muxing */
 	if(!r->p){
blob - 2c611e45929a2ee1d9e7486d569c2d8125ced681
blob + fb4c674667cb33586e8ca767c7ec208a20223ea5
--- src/libthread/386.c
+++ src/libthread/386.c
@@ -1,3 +1,7 @@
+#include "ucontext.c"
+
+#ifdef OLD
+
 #include "threadimpl.h"
 /*
  * To use this you need some patches to Valgrind that
@@ -55,3 +59,4 @@ _threadstacklimit(void *bottom, void *top)
 	VALGRIND_SET_STACK_LIMIT(1, bottom, top);
 #endif
 }
+#endif
blob - a715706ab47c08fa86198623ab542a75172fd71c
blob + 8768e60df715c4d78e23a552a8977bcebabf3f1b
--- src/libthread/fdwait.c
+++ src/libthread/fdwait.c
@@ -241,15 +241,16 @@ threadread(int fd, void *a, long n)
 
 	threadfdnoblock(fd);
 again:
+	/*
+	 * Always call wait (i.e. don't optimistically try the read)
+	 * so that the scheduler gets a chance to run other threads.
+	 */
+	_threadfdwait(fd, 'r', getcallerpc(&fd));
 	errno = 0;
 	nn = read(fd, a, n);
 	if(nn <= 0){
-		if(errno == EINTR)
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
 			goto again;
-		if(errno == EAGAIN || errno == EWOULDBLOCK){
-			_threadfdwait(fd, 'r', getcallerpc(&fd));
-			goto again;
-		}
 	}
 	return nn;
 }
@@ -261,14 +262,15 @@ threadrecvfd(int fd)
 
 	threadfdnoblock(fd);
 again:
+	/*
+	 * Always call wait (i.e. don't optimistically try the recvfd)
+	 * so that the scheduler gets a chance to run other threads.
+	 */
+	_threadfdwait(fd, 'r', getcallerpc(&fd));
 	nn = recvfd(fd);
 	if(nn < 0){
-		if(errno == EINTR)
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
 			goto again;
-		if(errno == EAGAIN || errno == EWOULDBLOCK){
-			_threadfdwait(fd, 'r', getcallerpc(&fd));
-			goto again;
-		}
 	}
 	return nn;
 }
@@ -280,14 +282,15 @@ threadsendfd(int fd, int sfd)
 
 	threadfdnoblock(fd);
 again:
+	/*
+	 * Always call wait (i.e. don't optimistically try the sendfd)
+	 * so that the scheduler gets a chance to run other threads.
+	 */
+	_threadfdwait(fd, 'w', getcallerpc(&fd));
 	nn = sendfd(fd, sfd);
 	if(nn < 0){
-		if(errno == EINTR)
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
 			goto again;
-		if(errno == EAGAIN || errno == EWOULDBLOCK){
-			_threadfdwait(fd, 'w', getcallerpc(&fd));
-			goto again;
-		}
 	}
 	return nn;
 }
@@ -315,14 +318,15 @@ _threadwrite(int fd, const void *a, long n)
 
 	threadfdnoblock(fd);
 again:
+	/*
+	 * Always call wait (i.e. don't optimistically try the write)
+	 * so that the scheduler gets a chance to run other threads.
+	 */
+	_threadfdwait(fd, 'w', getcallerpc(&fd));
 	nn = write(fd, a, n);
 	if(nn < 0){
-		if(errno == EINTR)
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
 			goto again;
-		if(errno == EAGAIN || errno == EWOULDBLOCK){
-			_threadfdwait(fd, 'w', getcallerpc(&fd));
-			goto again;
-		}
 	}
 	return nn;
 }
blob - c3cef2d029cc2216a680a713c0f668cc441cc95d
blob + 5081f48f621705bc1ca9e3f0defa29d8f54e0984
--- src/libthread/label.h
+++ src/libthread/label.h
@@ -7,9 +7,15 @@
 typedef struct Label Label;
 #define LABELDPC 0
 
-#if defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__) || defined(__OpenBSD__))
+#if defined(__linux__)
+#include <ucontext.h>
 struct Label
 {
+	ucontext_t uc;
+};
+#elif defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__) || defined(__OpenBSD__))
+struct Label
+{
 	ulong pc;
 	ulong bx;
 	ulong sp;
blob - 679a334bc6d464a9f359f94e1ab243ac9ee03c70
blob + 8cdd8ca3628c0a61243386de7aad8ea283c3bdfd
--- src/libthread/main.c
+++ src/libthread/main.c
@@ -50,7 +50,7 @@ main(int argc, char **argv)
 	a->argc = argc;
 	a->argv = argv;
 	p = _newproc(mainlauncher, a, mainstacksize, "threadmain", 0, 0);
-	_schedinit(p);
+	_scheduler(p);
 	abort();	/* not reached */
 	return 0;
 }
@@ -80,7 +80,7 @@ _schedfork(Proc *p)
 {
 	int pid;
 	lock(&p->lock);
-	pid = ffork(RFMEM|RFNOWAIT, _schedinit, p);
+	pid = ffork(RFMEM|RFNOWAIT, _scheduler, p);
 	p->pid = pid;
 	unlock(&p->lock);
 	return pid;
blob - 81cc34394e2c747d9008341651f765687f752a50
blob + 7f7b6daa4d1dcb0783a7518425a1000522b72b23
--- src/libthread/sched.c
+++ src/libthread/sched.c
@@ -3,7 +3,7 @@
 #include <errno.h>
 #include "threadimpl.h"
 
-//static Thread	*runthread(Proc*);
+static Thread	*runthread(Proc*);
 
 static char *_psstate[] = {
 	"Dead",
@@ -21,27 +21,54 @@ psstate(int s)
 }
 
 void
-_schedinit(void *arg)
+needstack(int howmuch)
 {
 	Proc *p;
 	Thread *t;
-	extern void ignusr1(int), _threaddie(int);
-	signal(SIGTERM, _threaddie);
-  
+
+	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)
+{
+	Proc *p;
+	Thread *t;
+
 	p = arg;
 	lock(&p->lock);
 	p->pid = _threadgetpid();
 	_threadsetproc(p);
-	unlock(&p->lock);
-	while(_setlabel(&p->sched))
-		;
-	_threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _threadexitsallstatus);
-	if(_threadexitsallstatus)
-		_exits(_threadexitsallstatus);
-	lock(&p->lock);
-	if((t=p->thread) != nil){
+
+	for(;;){
+		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);
+
+		lock(&p->lock);
 		p->thread = nil;
 		if(t->moribund){
+		Moribund:
 			if(t->moribund != 1)
 				fprint(2, "moribund %d\n", t->moribund);
 			assert(t->moribund == 1);
@@ -65,7 +92,8 @@ _schedinit(void *arg)
 			free(t);	/* XXX how do we know there are no references? */
 			p->nthreads--;
 			t = nil;
-			_sched();
+			lock(&p->lock);
+			continue;
 		}
 /*
 		if(p->needexec){
@@ -78,15 +106,27 @@ _schedinit(void *arg)
 			if(t->ret < 0){
 //fprint(2, "_schedfork: %r\n");
 				abort();
-}
+			}
 			p->newproc = nil;
 		}
 		t->state = t->nextstate;
 		if(t->state == Ready)
 			_threadready(t);
+		unlock(&p->lock);
 	}
-	unlock(&p->lock);
-	_sched();
+}
+
+int
+_sched(void)
+{
+	Proc *p;
+	Thread *t;
+
+	p = _threadgetproc();
+	t = p->thread;
+	assert(t != nil);
+	_swaplabel(&t->sched, &p->sched);
+	return p->nsched++;
 }
 
 static Thread*
@@ -157,58 +197,6 @@ relock:
 	return t;
 }
 
-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();
-	}
-}
-
-int
-_sched(void)
-{
-	Proc *p;
-	Thread *t;
-
-Resched:
-	p = _threadgetproc();
-//fprint(2, "p %p\n", p);
-	if((t = p->thread) != nil){
-		needstack(512);
-	//	_threaddebug(DBGSCHED, "pausing, state=%s set %p goto %p",
-	//		psstate(t->state), &t->sched, &p->sched);
-		if(_setlabel(&t->sched)==0)
-			_gotolabel(&p->sched);
-		_threadstacklimit(t->stk, t->stk+t->stksize);
-		return p->nsched++;
-	}else{
-		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 Resched;
-		}
-		t->state = Running;
-		t->nextstate = Ready;
-		_gotolabel(&t->sched);
-		for(;;);
-	}
-}
-
 long
 threadstack(void)
 {
blob - d6a2390582cf3777b53311de800c9ea7b9cd3c94
blob + fea309d92788edbaf073c6f8189da97ef7e64c11
--- src/libthread/threadimpl.h
+++ src/libthread/threadimpl.h
@@ -167,8 +167,7 @@ struct Ioproc
 	Ioproc *next;
 };
 
-void		_gotolabel(Label*);
-int		_setlabel(Label*);
+void		_swaplabel(Label*, Label*);
 void		_freeproc(Proc*);
 Proc*	_newproc(void(*)(void*), void*, uint, char*, int, int);
 int		_procsplhi(void);
@@ -178,7 +177,7 @@ int		_schedexec(Execargs*);
 void		_schedexecwait(void);
 void		_schedexit(Proc*);
 int		_schedfork(Proc*);
-void		_schedinit(void*);
+void		_scheduler(void*);
 void		_systhreadinit(void);
 void		_threadassert(char*);
 void		__threaddebug(ulong, char*, ...);
blob - /dev/null
blob + 60f803d3cfaeb62186deed0c6ed821d179e8e3ed (mode 644)
--- /dev/null
+++ src/libthread/ucontext.c
@@ -0,0 +1,41 @@
+#include "threadimpl.h"
+
+void
+_threadinitstack(Thread *t, void (*f)(void*), void *arg)
+{
+	sigset_t zero;
+
+	/* do a reasonable initialization */
+	memset(&t->sched.uc, 0, sizeof t->sched.uc);
+	sigemptyset(&zero);
+	sigprocmask(SIG_BLOCK, &zero, &t->sched.uc.uc_sigmask);
+
+	/* call getcontext, because on Linux makecontext neglects floating point */
+	getcontext(&t->sched.uc);
+
+	/* call makecontext to do the real work. */
+	t->sched.uc.uc_stack.ss_sp = t->stk;
+	t->sched.uc.uc_stack.ss_size = t->stksize;
+	makecontext(&t->sched.uc, (void(*)())f, 1, arg);
+}
+
+void
+_threadinswitch(int enter)
+{
+	USED(enter);
+}
+
+void
+_threadstacklimit(void *bottom, void *top)
+{
+	USED(bottom);
+	USED(top);
+}
+
+void
+_swaplabel(Label *old, Label *new)
+{
+	if(swapcontext(&old->uc, &new->uc) < 0)
+		sysfatal("swapcontext: %r");
+}
+
blob - bed2755a82148f8cca407a464ad5ae89359f0528
blob + 87df6c14654f20eb2df69eeafc21ee1f5eabd47c
--- src/mkfile
+++ src/mkfile
@@ -9,6 +9,14 @@ DIRS=\
 
 <mkdirs
 
+libs:V: libs-all
+
+libs-%:V:
+	for i in $LIBDIRS
+	do
+		(cd $i; mk $stem)
+	done
+
 MKDIRS=\
 	libbio\
 	libregexp\