tContinue the pthreads torture. - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit c6687d4591964cb13df87f55ec4770e778a4a55c
 (DIR) parent 3d5e34e146b5ba5c973230abb624ce9126024569
 (HTM) Author: rsc <devnull@localhost>
       Date:   Tue, 21 Sep 2004 01:11:28 +0000
       
       Continue the pthreads torture.
       
       Diffstat:
         M include/libc.h                      |       1 +
         M include/u.h                         |       7 ++++---
         M src/cmd/9term/9term.c               |       8 ++------
         M src/cmd/9term/rcstart.c             |      26 ++++++++++++++++++++++++--
         M src/lib9/9proc.h                    |       3 ---
         A src/lib9/_p9proc-Linux.c            |       5 +++++
         A src/lib9/_p9proc-getpid.c           |     113 +++++++++++++++++++++++++++++++
         M src/lib9/fork.c                     |      11 +++++++++--
         A src/lib9/lock-Darwin.c              |       1 +
         A src/lib9/lock-FreeBSD.c             |       1 +
         A src/lib9/lock-Linux.c               |       5 +++++
         A src/lib9/lock-pthread.c             |      54 +++++++++++++++++++++++++++++++
         A src/lib9/lock-tas.c                 |      57 +++++++++++++++++++++++++++++++
         M src/lib9/mkfile                     |       5 +++--
         M src/lib9/qlock.c                    |      22 ++++++++++++----------
         M src/lib9/rendez-signal.c            |       1 +
         M src/libmux/mux.c                    |       3 ++-
         M src/libthread/386.c                 |       5 +++++
         M src/libthread/fdwait.c              |      44 +++++++++++++++++--------------
         M src/libthread/label.h               |       8 +++++++-
         M src/libthread/main.c                |       4 ++--
         M src/libthread/sched.c               |     126 ++++++++++++++-----------------
         M src/libthread/threadimpl.h          |       5 ++---
         A src/libthread/ucontext.c            |      41 +++++++++++++++++++++++++++++++
         M src/mkfile                          |       8 ++++++++
       
       25 files changed, 440 insertions(+), 124 deletions(-)
       ---
 (DIR) diff --git a/include/libc.h b/include/libc.h
       t@@ -436,6 +436,7 @@ struct Lock
        #endif
        };
        
       +extern        int        _tas(int*);
        extern        void        lock(Lock*);
        extern        void        unlock(Lock*);
        extern        int        canlock(Lock*);
 (DIR) diff --git a/include/u.h b/include/u.h
       t@@ -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
       t@@ -42,8 +41,10 @@ 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__)
 (DIR) diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
       t@@ -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;
                }
        
 (DIR) diff --git a/src/cmd/9term/rcstart.c b/src/cmd/9term/rcstart.c
       t@@ -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)
        {
       t@@ -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");
 (DIR) diff --git a/src/lib9/9proc.h b/src/lib9/9proc.h
       t@@ -13,9 +13,6 @@ struct Uproc
                int pid;
                int state;
                void *priv[NPRIV];
       -        ulong rendval;
       -        ulong rendtag;
       -        Uproc *rendhash;
                p9jmp_buf notejb;
        };
        
 (DIR) diff --git a/src/lib9/_p9proc-Linux.c b/src/lib9/_p9proc-Linux.c
       t@@ -0,0 +1,5 @@
       +#ifdef __Linux26__
       +#include "_p9proc-pthread.c"
       +#else
       +#include "_p9proc-getpid.c"
       +#endif
 (DIR) diff --git a/src/lib9/_p9proc-getpid.c b/src/lib9/_p9proc-getpid.c
       t@@ -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;
       +                }
       +        }
       +}
 (DIR) diff --git a/src/lib9/fork.c b/src/lib9/fork.c
       t@@ -1,4 +1,5 @@
        #include <u.h>
       +#include <signal.h>
        #include <libc.h>
        #include "9proc.h"
        #undef fork
       t@@ -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;
        }
 (DIR) diff --git a/src/lib9/lock-Darwin.c b/src/lib9/lock-Darwin.c
       t@@ -0,0 +1 @@
       +#include "lock-pthread.c"
 (DIR) diff --git a/src/lib9/lock-FreeBSD.c b/src/lib9/lock-FreeBSD.c
       t@@ -0,0 +1 @@
       +#include "lock-tas.c"
 (DIR) diff --git a/src/lib9/lock-Linux.c b/src/lib9/lock-Linux.c
       t@@ -0,0 +1,5 @@
       +#ifdef __Linux26__
       +#include "lock-pthread.c"
       +#else
       +#include "lock-tas.c"
       +#endif
 (DIR) diff --git a/src/lib9/lock-pthread.c b/src/lib9/lock-pthread.c
       t@@ -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();
       +}
 (DIR) diff --git a/src/lib9/lock-tas.c b/src/lib9/lock-tas.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/lib9/mkfile b/src/lib9/mkfile
       t@@ -67,7 +67,7 @@ LIB9OFILES=\
                _exits.$O\
                _p9dialparse.$O\
                _p9dir.$O\
       -        _p9proc.$O\
       +        _p9proc-$SYSNAME.$O\
                announce.$O\
                argv0.$O\
                atexit.$O\
       t@@ -111,7 +111,7 @@ LIB9OFILES=\
                jmp.$O\
                lrand.$O\
                lnrand.$O\
       -        lock.$O\
       +        lock-$SYSNAME.$O\
                main.$O\
                malloc.$O\
                malloctag.$O\
       t@@ -141,6 +141,7 @@ LIB9OFILES=\
                strecpy.$O\
                sysfatal.$O\
                sysname.$O\
       +        tas-$OBJTYPE.$O\
                time.$O\
                tokenize.$O\
                truerand.$O\
 (DIR) diff --git a/src/lib9/qlock.c b/src/lib9/qlock.c
       t@@ -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
       t@@ -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);
       t@@ -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;
                }
       t@@ -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);
       t@@ -181,7 +183,7 @@ runlock(RWLock *q)
        
                /* wakeup waiter */
                p->state = Waking;
       -        _procwakeup(&p->rend);
       +        procwakeup(&p->rend);
                unlock(&q->lock);
        }
        
       t@@ -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);
       t@@ -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;
                }
       t@@ -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;
       t@@ -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
 (DIR) diff --git a/src/lib9/rendez-signal.c b/src/lib9/rendez-signal.c
       t@@ -52,6 +52,7 @@ _procsleep(_Procrend *r)
                 */
                ignusr1(1);
                assert(r->asleep == 0);
       +        lock(r->l);
        }
        
        void
 (DIR) diff --git a/src/libmux/mux.c b/src/libmux/mux.c
       t@@ -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){
 (DIR) diff --git a/src/libthread/386.c b/src/libthread/386.c
       t@@ -1,3 +1,7 @@
       +#include "ucontext.c"
       +
       +#ifdef OLD
       +
        #include "threadimpl.h"
        /*
         * To use this you need some patches to Valgrind that
       t@@ -55,3 +59,4 @@ _threadstacklimit(void *bottom, void *top)
                VALGRIND_SET_STACK_LIMIT(1, bottom, top);
        #endif
        }
       +#endif
 (DIR) diff --git a/src/libthread/fdwait.c b/src/libthread/fdwait.c
       t@@ -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;
        }
       t@@ -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;
        }
       t@@ -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;
        }
       t@@ -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;
        }
 (DIR) diff --git a/src/libthread/label.h b/src/libthread/label.h
       t@@ -7,7 +7,13 @@
        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;
 (DIR) diff --git a/src/libthread/main.c b/src/libthread/main.c
       t@@ -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;
        }
       t@@ -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;
 (DIR) diff --git a/src/libthread/sched.c b/src/libthread/sched.c
       t@@ -3,7 +3,7 @@
        #include <errno.h>
        #include "threadimpl.h"
        
       -//static Thread        *runthread(Proc*);
       +static Thread        *runthread(Proc*);
        
        static char *_psstate[] = {
                "Dead",
       t@@ -21,27 +21,54 @@ psstate(int s)
        }
        
        void
       -_schedinit(void *arg)
       +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)
        {
                Proc *p;
                Thread *t;
       -        extern void ignusr1(int), _threaddie(int);
       -        signal(SIGTERM, _threaddie);
       -  
       +
                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);
       t@@ -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){
       t@@ -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*
       t@@ -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)
        {
 (DIR) diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h
       t@@ -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);
       t@@ -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*, ...);
 (DIR) diff --git a/src/libthread/ucontext.c b/src/libthread/ucontext.c
       t@@ -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");
       +}
       +
 (DIR) diff --git a/src/mkfile b/src/mkfile
       t@@ -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\