tmore thread work - 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 195645536743aeb99eb336726823c38716cec02d
 (DIR) parent 77dcf88474c55e040940be8a5f9e7fa1537af564
 (HTM) Author: rsc <devnull@localhost>
       Date:   Mon,  8 Nov 2004 16:03:20 +0000
       
       more thread work
       
       Diffstat:
         M src/libthread/channel.c             |      18 +++++-------------
         M src/libthread/create.c              |       2 +-
         M src/libthread/exec-unix.c           |      18 +++---------------
         M src/libthread/exit.c                |       2 +-
         M src/libthread/fdwait.c              |     159 +++----------------------------
         M src/libthread/main.c                |      69 +++++++++++---------------------
         M src/libthread/mkfile                |      12 ++++++------
         M src/libthread/pthread.c             |     268 +++++++++++++++++++++----------
         M src/libthread/sched.c               |      37 ++++++++++++++++++++++++++-----
         M src/libthread/sleep.c               |       1 +
         M src/libthread/thread.sh             |       8 ++++----
         M src/libthread/threadimpl.h          |      34 +++++++++++++++++++++----------
       
       12 files changed, 291 insertions(+), 337 deletions(-)
       ---
 (DIR) diff --git a/src/libthread/channel.c b/src/libthread/channel.c
       t@@ -4,7 +4,7 @@ static Lock chanlock;                /* central channel access lock */
        
        static void enqueue(Alt*, Thread*);
        static void dequeue(Alt*);
       -static int altexec(Alt*, int);
       +static int altexec(Alt*);
        
        int _threadhighnentry;
        int _threadnalt;
       t@@ -101,7 +101,7 @@ _alt(Alt *alts)
        {
                Alt *a, *xa;
                Channel *c;
       -        int n, s;
       +        int n;
                Thread *t;
        
                /*
       t@@ -119,7 +119,6 @@ _alt(Alt *alts)
                t = _threadgetproc()->thread;
                if((t && t->moribund) || _threadexitsallstatus)
                        yield();        /* won't return */
       -        s = _procsplhi();
                lock(&chanlock);
        
                /* test whether any channels can proceed */
       t@@ -134,7 +133,6 @@ _alt(Alt *alts)
                        c = xa->c;
                        if(c==nil){
                                unlock(&chanlock);
       -                        _procsplx(s);
                                return -1;
                        }
                        if(canexec(xa))
       t@@ -146,8 +144,7 @@ _alt(Alt *alts)
                        /* nothing can proceed */
                        if(xa->op == CHANNOBLK){
                                unlock(&chanlock);
       -                        _procsplx(s);
       -_threadnalt++;
       +                        _threadnalt++;
                                return xa - alts;
                        }
        
       t@@ -172,9 +169,7 @@ _threadnalt++;
                        t->alt = alts;
                        t->chan = Chanalt;
                        t->altrend.l = &chanlock;
       -                _procsplx(s);
                        _threadsleep(&t->altrend);
       -                s = _procsplhi();
        
                        /* dequeue from channels, find selected one */
                        a = nil;
       t@@ -187,13 +182,12 @@ _threadnalt++;
                                dequeue(xa);
                        }
                        unlock(&chanlock);
       -                _procsplx(s);
                        if(a == nil){        /* we were interrupted */
                                assert(c==(Channel*)~0);
                                return -1;
                        }
                }else{
       -                altexec(a, s);        /* unlocks chanlock, does splx */
       +                altexec(a);        /* unlocks chanlock, does splx */
                }
                if(t)
                        t->chan = Channone;
       t@@ -445,7 +439,7 @@ altcopy(void *dst, void *src, int sz)
        }
        
        static int
       -altexec(Alt *a, int spl)
       +altexec(Alt *a)
        {
                volatile Alt *b;
                int i, n, otherop;
       t@@ -492,7 +486,6 @@ altexec(Alt *a, int spl)
                        _threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)(void*)&chanlock);
                        _threaddebug(DBGCHAN, "unlocking the chanlock");
                        unlock(&chanlock);
       -                _procsplx(spl);
                        return 1;
                }
        
       t@@ -503,6 +496,5 @@ altexec(Alt *a, int spl)
                        altcopy(buf, me, c->e);
        
                unlock(&chanlock);
       -        _procsplx(spl);
                return 1;
        }
 (DIR) diff --git a/src/libthread/create.c b/src/libthread/create.c
       t@@ -138,7 +138,7 @@ proccreate(void (*f)(void*), void *arg, uint stacksize)
                p = _threadgetproc();
                np = _newproc();
                p->newproc = np;
       -        p->schedfn = _threadstartproc;
       +        p->schedfn = _kthreadstartproc;
                id = _newthread(np, f, arg, stacksize, nil, p->thread->grp);
                _sched();        /* call into scheduler to create proc XXX */
                return id;
 (DIR) diff --git a/src/libthread/exec-unix.c b/src/libthread/exec-unix.c
       t@@ -10,20 +10,9 @@ _threadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
                int pfd[2];
                int n, pid;
                char exitstr[ERRMAX];
       -        static int firstexec = 1;
       -        static Lock lk;
        
                _threaddebug(DBGEXEC, "threadexec %s", prog);
        
       -        if(firstexec){
       -                lock(&lk);
       -                if(firstexec){
       -                        firstexec = 0;
       -                        _threadfirstexec();
       -                }
       -                unlock(&lk);
       -        }
       -
                /*
                 * We want threadexec to behave like exec; if exec succeeds,
                 * never return, and if it fails, return with errstr set.
       t@@ -53,7 +42,6 @@ _threadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
                        _threaddebug(DBGSCHED, "exit after efork");
                        _exit(0);
                default:
       -                _threadafterexec();
                        if(freeargs)
                                free(args);
                        break;
       t@@ -88,14 +76,14 @@ Bad:
        void
        threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
        {
       -        if(_callthreadexec(pidc, fd, prog, args, 0) >= 0)
       +        if(_kthreadexec(pidc, fd, prog, args, 0) >= 0)
                        threadexits(nil);
        }
        
        int
        threadspawn(int fd[3], char *prog, char *args[])
        {
       -        return _callthreadexec(nil, fd, prog, args, 0);
       +        return _kthreadexec(nil, fd, prog, args, 0);
        }
        
        /*
       t@@ -128,7 +116,7 @@ threadexecl(Channel *pidc, int fd[3], char *f, ...)
                args[n] = 0;
                va_end(arg);
        
       -        if(_callthreadexec(pidc, fd, f, args, 1) >= 0)
       +        if(_kthreadexec(pidc, fd, f, args, 1) >= 0)
                        threadexits(nil);
        }
        
 (DIR) diff --git a/src/libthread/exit.c b/src/libthread/exit.c
       t@@ -32,7 +32,7 @@ threadexitsall(char *exitstr)
                _threadexitsallstatus = exitstr;
                _threaddebug(DBGSCHED, "_threadexitsallstatus set to %p", _threadexitsallstatus);
                /* leave */
       -        _threadexitallproc(exitstr);
       +        _kthreadexitallproc(exitstr);
        }
        
        Channel*
 (DIR) diff --git a/src/libthread/fdwait.c b/src/libthread/fdwait.c
       t@@ -7,164 +7,25 @@
        #include <unistd.h>
        #include <fcntl.h>
        
       -#define debugpoll 0
       -
       -#ifdef __APPLE__
       -#include <sys/time.h>
       -enum { POLLIN=1, POLLOUT=2, POLLERR=4 };
       -struct pollfd
       -{
       -        int fd;
       -        int events;
       -        int revents;
       -};
       -
       -int
       -poll(struct pollfd *p, int np, int ms)
       +void
       +fdwait()
        {
       -        int i, maxfd, n;
       -        struct timeval tv, *tvp;
                fd_set rfd, wfd, efd;
       -        
       -        maxfd = -1;
       +
                FD_ZERO(&rfd);
                FD_ZERO(&wfd);
                FD_ZERO(&efd);
       -        for(i=0; i<np; i++){
       -                p[i].revents = 0;
       -                if(p[i].fd == -1)
       -                        continue;
       -                if(p[i].fd > maxfd)
       -                        maxfd = p[i].fd;
       -                if(p[i].events & POLLIN)
       -                        FD_SET(p[i].fd,        &rfd);
       -                if(p[i].events & POLLOUT)
       -                        FD_SET(p[i].fd, &wfd);
       -                FD_SET(p[i].fd, &efd);
       -        }
       -
       -        if(ms != -1){
       -                tv.tv_usec = (ms%1000)*1000;
       -                tv.tv_sec = ms/1000;
       -                tvp = &tv;
       -        }else
       -                tvp = nil;
       -
       -        if(debugpoll){
       -                fprint(2, "select %d:", maxfd+1);
       -                for(i=0; i<=maxfd; i++){
       -                        if(FD_ISSET(i, &rfd))
       -                                fprint(2, " r%d", i);
       -                        if(FD_ISSET(i, &wfd))
       -                                fprint(2, " w%d", i);
       -                        if(FD_ISSET(i, &efd))
       -                                fprint(2, " e%d", i);
       -                }
       -                fprint(2, "; tp=%p, t=%d.%d\n", tvp, tv.tv_sec, tv.tv_usec);
       -        }
       -
       -        n = select(maxfd+1, &rfd, &wfd, &efd, tvp);
       -
       -        if(n <= 0)
       -                return n;
       -
       -        for(i=0; i<np; i++){
       -                if(p[i].fd == -1)
       -                        continue;
       -                if(FD_ISSET(p[i].fd, &rfd))
       -                        p[i].revents |= POLLIN;
       -                if(FD_ISSET(p[i].fd, &wfd))
       -                        p[i].revents |= POLLOUT;
       -                if(FD_ISSET(p[i].fd, &efd))
       -                        p[i].revents |= POLLERR;
       -        } 
       -        return n;
       -}
       -
       -#else
       -#include <poll.h>
       -#endif
       -
       -/*
       - * Poll file descriptors in an idle loop.
       - */
       -
       -typedef struct Poll Poll;
       -
       -struct Poll
       -{
       -        Channel *c;        /* for sending back */
       -};
       -
       -static Channel *sleepchan[64];
       -static int sleeptime[64];
       -static int nsleep;
       -
       -static struct pollfd pfd[64];
       -static struct Poll polls[64];
       -static int npoll;
       -
       -static void
       -pollidle(void *v)
       -{
       -        int i, n, t;
       -        uint now;
       -
       -        for(;; yield()){
       -                if(debugpoll) fprint(2, "poll %d:", npoll);
       -                for(i=0; i<npoll; i++){
       -                        if(debugpoll) fprint(2, " %d%c", pfd[i].fd, pfd[i].events==POLLIN ? 'r' : 'w');
       -                        pfd[i].revents = 0;
       -                }
       -                t = -1;
       -                now = p9nsec()/1000000;
       -                for(i=0; i<nsleep; i++){
       -                        n = sleeptime[i] - now;
       -                        if(debugpoll) fprint(2, " s%d", n);
       -                        if(n < 0)
       -                                n = 0;
       -                        if(t == -1 || n < t)
       -                                t = n;
       -                }
       -                if(debugpoll) fprint(2, "; t=%d\n", t);
       -        
       -                n = poll(pfd, npoll, t);
       -                //fprint(2, "poll ret %d:", n);
       -                now = p9nsec()/1000000;
       -                for(i=0; i<nsleep; i++){
       -                        if((int)(sleeptime[i] - now) < 0){
       -                                nbsendul(sleepchan[i], 0);
       -                                nsleep--;
       -                                sleepchan[i] = sleepchan[nsleep];
       -                                sleeptime[i] = sleeptime[nsleep];
       -                                i--;
       -                        }
       -                }
       -                                
       -                if(n <= 0)
       -                        continue;
       -                for(i=0; i<npoll; i++)
       -                        if(pfd[i].fd != -1 && pfd[i].revents){
       -                                //fprint(2, " %d", pfd[i].fd);
       -                                pfd[i].fd = -1;
       -                                pfd[i].events = 0;
       -                                pfd[i].revents = 0;
       -                                nbsendul(polls[i].c, 1);
       -                                //fprint(2, " x%d", pfd[i].fd);
       -                        }
       -                //fprint(2, "\n");
       -        }
       +        if(mode=='w')
       +                FD_SET(&wfd, fd);
       +        else
       +                FD_SET(&rfd, fd);
       +        FD_SET(&efd, fd);
       +        select(fd+1, &rfd, &wfd, &efd, nil);
        }
        
        void
        threadfdwaitsetup(void)
        {
       -        static int setup = 0;
       -
       -        if(!setup){
       -                setup = 1;
       -                proccreate(pollidle, nil, 16384);
       -        }
        }
        
        void
       t@@ -199,6 +60,8 @@ _threadfdwait(int fd, int rw, ulong pc)
                polls[i].c = &s.c;
                if(0) fprint(2, "%s [%3d] fdwait %d %c list *0x%lux\n",
                        argv0, threadid(), fd, rw, pc);
       +        if(pollpid)
       +                postnote(PNPROC, pollpid, "interrupt");
                recvul(&s.c);
        }
        
 (DIR) diff --git a/src/libthread/main.c b/src/libthread/main.c
       t@@ -11,12 +11,10 @@ struct Mainarg
                char **argv;
        };
        
       -int _threadmainpid;
        int mainstacksize;
       -int _callsthreaddaemonize;
       -static int passtomainpid;
        
        extern void (*_sysfatal)(char*, va_list);
       +extern Jmp *(*_notejmpbuf)(void);
        
        static void
        mainlauncher(void *arg)
       t@@ -24,58 +22,24 @@ mainlauncher(void *arg)
                Mainarg *a;
        
                a = arg;
       -        _threadmaininit();
       +        _kmaininit();
                threadmain(a->argc, a->argv);
                threadexits("threadmain");
        }
        
       -static void
       -passer(void *x, char *msg)
       -{
       -        USED(x);
       -        Waitmsg *w;
       -
       -        if(strcmp(msg, "sys: usr2") == 0)
       -                _exit(0);        /* daemonize */
       -        else if(strcmp(msg, "sys: child") == 0){
       -                w = wait();
       -                if(w == nil)
       -                        _exit(1);
       -                _exit(atoi(w->msg));
       -        }else
       -                postnote(PNPROC, passtomainpid, msg);
       -}
       -
        int
        main(int argc, char **argv)
        {
       -        int pid;
                Mainarg a;
                Proc *p;
       -        sigset_t mask;
        
                /*
       -         * Do daemonize hack here.
       +         * In pthreads, threadmain is actually run in a subprocess,
       +         * so that the main process can exit (if threaddaemonize is called).
       +         * The main process relays notes to the subprocess.
       +         * _Threadbackgroundsetup will return only in the subprocess.
                 */
       -        if(_callsthreaddaemonize){
       -                passtomainpid = getpid();
       -                switch(pid = fork()){
       -                case -1:
       -                        sysfatal("fork: %r");
       -
       -                case 0:
       -                        /* continue executing */
       -                        _threadmainpid = getppid();
       -                        break;
       -
       -                default:
       -                        /* wait for signal USR2 */
       -                        notify(passer);
       -                        for(;;)
       -                                pause();
       -                        _exit(0);
       -                }
       -        }
       +        _threadbackgroundinit();
        
                /*
                 * Instruct QLock et al. to use our scheduling functions
       t@@ -85,16 +49,17 @@ main(int argc, char **argv)
        
                /*
                 * Install our own _threadsysfatal which takes down
       -         * the whole conglomeration of procs.
       +         * the whole confederation of procs.
                 */
                _sysfatal = _threadsysfatal;
        
                /*
       -         * XXX Install our own jump handler.
       +         * Install our own jump handler.
                 */
       +        _notejmpbuf = _threadgetjmp;
        
                /*
       -         * Install our own signal handlers.
       +         * Install our own signal handler.
                 */
                notify(_threadnote);
        
       t@@ -119,3 +84,15 @@ void
        _threadlinkmain(void)
        {
        }
       +
       +Jmp*
       +_threadgetjmp(void)
       +{
       +        static Jmp j;
       +        Proc *p;
       +
       +        p = _threadgetproc();
       +        if(p == nil)
       +                return &j;
       +        return &p->sigjmp;
       +}
 (DIR) diff --git a/src/libthread/mkfile b/src/libthread/mkfile
       t@@ -1,15 +1,12 @@
        <$PLAN9/src/mkhdr
        
        LIB=libthread.a
       -THREAD=`sh ./thread.sh`
       +SYSOFILES=`sh ./sysofiles.sh`
        OFILES=\
       -        $OBJTYPE.$O\
       -        $THREAD.$O\
       -        asm-$SYSNAME-$OBJTYPE.$O\
       +        $SYSOFILES\
                channel.$O\
                chanprint.$O\
                create.$O\
       -        daemon.$O\
                debug.$O\
                exec-unix.$O\
                exit.$O\
       t@@ -29,10 +26,10 @@ OFILES=\
                main.$O\
                memset.$O\
                memsetd.$O\
       -        note.$O\
                read9pmsg.$O\
                ref.$O\
                sched.$O\
       +        setproc.$O\
                sleep.$O\
        
        HFILES=\
       t@@ -57,6 +54,9 @@ tspawn: tspawn.$O $PLAN9/lib/$LIB
        trend: trend.$O $PLAN9/lib/$LIB
                $LD -o trend trend.$O $LDFLAGS -lthread -l9
        
       +tsignal: tsignal.$O $PLAN9/lib/$LIB
       +        $LD -o tsignal tsignal.$O $LDFLAGS -lthread -l9
       +
        CLEANFILES=$CLEANFILES tprimes texec
        
        asm-Linux-ppc.$O: asm-Linux-386.s
 (DIR) diff --git a/src/libthread/pthread.c b/src/libthread/pthread.c
       t@@ -2,53 +2,36 @@
        #include <errno.h>
        #include "threadimpl.h"
        
       -static int multi;
       -static Proc *theproc;
       -static pthread_key_t key;
       -
        /*
       - * Called before we go multiprocess.
       + * Basic kernel thread management.
         */
       +static pthread_key_t key;
       +
        void
       -_threadmultiproc(void)
       +_kthreadinit(void)
        {
       -        if(multi == 0){
       -                multi = 1;
       -                pthread_key_create(&key, 0);
       -                _threadsetproc(theproc);
       -        }
       +        pthread_key_create(&key, 0);
        }
        
       -/*
       - * Set the proc for the current pthread.
       - */
        void
       -_threadsetproc(Proc *p)
       +_kthreadsetproc(Proc *p)
        {
       -        if(!multi){
       -                theproc = p;
       -                return;
       -        }
       +        sigset_t all;
       +
       +        p->pthreadid = pthread_self();
       +        sigfillset(&all);
       +        pthread_sigmask(SIG_SETMASK, &all, nil);
                pthread_setspecific(key, p);
        }
        
       -/* 
       - * Get the proc for the current pthread.
       - */
        Proc*
       -_threadgetproc(void)
       +_kthreadgetproc(void)
        {
       -        if(!multi)
       -                return theproc;
       -
                return pthread_getspecific(key);
        }
        
       -/*
       - * Called to start a new proc.
       - */
        void
       -_threadstartproc(Proc *p)
       +_kthreadstartproc(Proc *p)
        {
                Proc *np;
                pthread_t tid;
       t@@ -63,69 +46,43 @@ _threadstartproc(Proc *p)
                np->pthreadid = tid;
        }
        
       -/*
       - * Called to associate p with the current pthread.
       - */
        void
       -_threadinitproc(Proc *p)
       -{
       -        p->pthreadid = pthread_self();
       -        _threadsetproc(p);
       -}
       -
       -/*
       - * Called to exit the current pthread.
       - */
       -void
       -_threadexitproc(char *exitstr)
       +_kthreadexitproc(char *exitstr)
        {
                _threaddebug(DBGSCHED, "_pthreadexit");
                pthread_exit(nil);
        }
        
       -/*
       - * Called to exit all pthreads.
       - */
        void
       -_threadexitallproc(char *exitstr)
       +_kthreadexitallproc(char *exitstr)
        {
                _threaddebug(DBGSCHED, "_threadexitallproc");
                exits(exitstr);
        }
        
        /*
       - * Called to poll for any kids of this pthread.
       - * Wait messages aren't restricted to a particular
       - * pthread, so we have a separate proc responsible
       - * for them.  So this is a no-op.
       + * Exec.  Pthreads does the hard work of making it possible
       + * for any thread to do the waiting, so this is pretty easy.
       + * We create a separate proc whose job is to wait for children
       + * and deliver wait messages.
         */
       -void
       -_threadwaitkids(Proc *p)
       -{
       -}
       +static Channel *_threadexecwaitchan;
        
       -/*
       - * Separate process to wait for child messages.
       - * Also runs signal handlers.
       - */
       -static Channel *_threadexecchan;
        static void
        _threadwaitproc(void *v)
        {
                Channel *c;
                Waitmsg *w;
       -        sigset_t none;
        
       -        sigemptyset(&none);
       -        pthread_sigmask(SIG_SETMASK, &none, 0);
       +        _threadinternalproc();
        
                USED(v);
                
                for(;;){
                        w = wait();
                        if(w == nil){
       -                        if(errno == ECHILD)
       -                                recvul(_threadexecchan);
       +                        if(errno == ECHILD)        /* wait for more */
       +                                recvul(_threadexecwaitchan);
                                continue;
                        }
                        if((c = _threadwaitchan) != nil)
       t@@ -133,43 +90,182 @@ _threadwaitproc(void *v)
                        else
                                free(w);
                }
       -fprint(2, "_threadwaitproc exits\n");
       +        fprint(2, "_threadwaitproc exits\n");        /* not reached */
        }
        
       +
        /* 
       - * Called before the first exec.
       + * Call _threadexec in the right conditions.
         */
       -void
       -_threadfirstexec(void)
       +int
       +_kthreadexec(Channel *c, int fd[3], char *prog, char *args[], int freeargs)
        {
       +        static Lock lk;
       +        int rv;
       +
       +        if(!_threadexecwaitchan){
       +                lock(&lk);
       +                if(!_threadexecwaitchan){
       +                        _threadexecwaitchan = chancreate(sizeof(ulong), 1);
       +                        proccreate(_threadwaitproc, nil, 32*1024);
       +                }
       +                unlock(&lk);
       +        }
       +        rv = _threadexec(c, fd, prog, args, freeargs);
       +        nbsendul(_threadexecwaitchan, 1);
       +        return rv;
        }
        
        /*
       - * Called from mainlauncher before threadmain.
       + * Some threaded applications want to run in the background.
       + * Calling fork() and exiting in the parent will result in a child
       + * with a single pthread (if we are using pthreads), and will screw
       + * up our internal process info if we are using clone/rfork.
       + * Instead, apps should call threadbackground(), which takes
       + * care of this.
       + * 
       + * _threadbackgroundinit is called from main.
         */
       +
       +static int mainpid, passerpid;
       +
       +static void
       +passer(void *x, char *msg)
       +{
       +        Waitmsg *w;
       +
       +        USED(x);
       +        if(strcmp(msg, "sys: usr2") == 0)
       +                _exit(0);        /* daemonize */
       +        else if(strcmp(msg, "sys: child") == 0){
       +                /* child exited => so should we */
       +                w = wait();
       +                if(w == nil)
       +                        _exit(1);
       +                _exit(atoi(w->msg));
       +        }else
       +                postnote(PNGROUP, mainpid, msg);
       +}
       +
        void
       -_threadmaininit(void)
       +_threadbackgroundinit(void)
        {
       -        _threadexecchan = chancreate(sizeof(ulong), 1);
       -        proccreate(_threadwaitproc, nil, 32*1024);
       +        int pid;
       +        sigset_t mask;
       +
       +        sigfillset(&mask);
       +        pthread_sigmask(SIG_BLOCK, &mask, 0);
       +
       +return;
       +
       +        passerpid = getpid();
       +        switch(pid = fork()){
       +        case -1:
       +                sysfatal("fork: %r");
       +
       +        case 0:
       +                rfork(RFNOTEG);
       +                return;
       +
       +        default:
       +                break;
       +        }
       +
       +        mainpid = pid;
       +        notify(passer);
       +        notifyon("sys: child");
       +        notifyon("sys: usr2");        /* should already be on */
       +        for(;;)
       +                pause();
       +        _exit(0);
       +}
        
       -        /*
       -         * Sleazy: decrement threadnprocs so that 
       -         * the existence of the _threadwaitproc proc
       -         * doesn't keep us from exiting.
       -         */
       -        lock(&_threadpq.lock);
       -        --_threadnprocs;
       -        /* print("change %d -> %d\n", _threadnprocs+1, _threadnprocs); */
       -        unlock(&_threadpq.lock);
       +void
       +threadbackground(void)
       +{
       +        if(passerpid <= 1)
       +                return;
       +        postnote(PNPROC, passerpid, "sys: usr2");
        }
        
        /*
       - * Called after forking the exec child.
       + * Notes.
         */
       +Channel *_threadnotechan;
       +static ulong sigs;
       +static Lock _threadnotelk;
       +static void _threadnoteproc(void*);
       +extern int _p9strsig(char*);
       +extern char *_p9sigstr(int);
       +
       +Channel*
       +threadnotechan(void)
       +{
       +        if(_threadnotechan == nil){
       +                lock(&_threadnotelk);
       +                if(_threadnotechan == nil){
       +                        _threadnotechan = chancreate(sizeof(char*), 1);
       +                        proccreate(_threadnoteproc, nil, 32*1024);
       +                }
       +                unlock(&_threadnotelk);
       +        }
       +        return _threadnotechan;
       +}
       +
       +void
       +_threadnote(void *x, char *msg)
       +{
       +        USED(x);
       +
       +        if(_threadexitsallstatus)
       +                _kthreadexitproc(_threadexitsallstatus);
       +
       +        if(strcmp(msg, "sys: usr2") == 0)
       +                noted(NCONT);
       +
       +        if(_threadnotechan == nil)
       +                noted(NDFLT);
       +
       +        sigs |= 1<<_p9strsig(msg);
       +        noted(NCONT);
       +}
       +
       +void
       +_threadnoteproc(void *x)
       +{
       +        int i;
       +        sigset_t none;
       +        Channel *c;
       +
       +        _threadinternalproc();
       +        sigemptyset(&none);
       +        pthread_sigmask(SIG_SETMASK, &none, 0);
       +
       +        c = _threadnotechan;
       +        for(;;){
       +                if(sigs == 0)
       +                        pause();
       +                for(i=0; i<32; i++){
       +                        if((sigs&(1<<i)) == 0)
       +                                continue;
       +                        sigs &= ~(1<<i);
       +                        if(i == 0)
       +                                continue;
       +                        sendp(c, _p9sigstr(i));
       +                }
       +        }
       +}
       +
        void
       -_threadafterexec(void)
       +_threadschednote(void)
        {
       -        nbsendul(_threadexecchan, 1);
        }
        
       +void
       +_kmaininit(void)
       +{
       +        sigset_t all;
       +
       +        sigfillset(&all);
       +        pthread_sigmask(SIG_SETMASK, &all, 0);
       +}
 (DIR) diff --git a/src/libthread/sched.c b/src/libthread/sched.c
       t@@ -18,13 +18,12 @@ _threadscheduler(void *arg)
                p = arg;
        
                _threadlinkmain();
       -        _threadinitproc(p);
       +        _threadsetproc(p);
        
                for(;;){
                        /* 
                         * Clean up zombie children.
                         */
       -                _threadwaitkids(p);
        
                        /*
                         * Find next thread to run.
       t@@ -154,6 +153,7 @@ runthread(Proc *p)
                if(p->nthreads==0 || (p->nthreads==1 && p->idle))
                        return nil;
        
       +        _threadschednote();
                lock(&p->readylock);
                q = &p->ready;
                if(q->head == nil){
       t@@ -180,7 +180,10 @@ runthread(Proc *p)
                         */
                        q->asleep = 1;
                        p->rend.l = &p->readylock;
       -                _procsleep(&p->rend);
       +                while(q->asleep){
       +                        _procsleep(&p->rend);
       +                        _threadschednote();
       +                }
        
                        /*
                         * Maybe we were awakened to exit?
       t@@ -284,6 +287,25 @@ _threadsetidle(int id)
                unlock(&p->readylock);
        }
        
       +/*
       + * Mark proc as internal so that if all but internal procs exit, we exit.
       + */
       +void
       +_threadinternalproc(void)
       +{
       +        Proc *p;
       +
       +        p = _threadgetproc();
       +        if(p->internal)
       +                return;
       +        lock(&_threadpq.lock);
       +        if(p->internal == 0){
       +                p->internal = 1;
       +                --_threadnprocs;
       +        }
       +        unlock(&_threadpq.lock);
       +}
       +
        static void
        schedexit(Proc *p)
        {
       t@@ -301,7 +323,10 @@ schedexit(Proc *p)
                                break;
                        }
                }
       -        n = --_threadnprocs;
       +        if(p->internal)
       +                n = _threadnprocs;
       +        else
       +                n = --_threadnprocs;
                unlock(&_threadpq.lock);
        
                strncpy(ex, p->exitstr, sizeof ex);
       t@@ -309,10 +334,10 @@ schedexit(Proc *p)
                free(p);
                if(n == 0){
                        _threaddebug(DBGSCHED, "procexit; no more procs");
       -                _threadexitallproc(ex);
       +                _kthreadexitallproc(ex);
                }else{
                        _threaddebug(DBGSCHED, "procexit");
       -                _threadexitproc(ex);
       +                _kthreadexitproc(ex);
                }
        }
        
 (DIR) diff --git a/src/libthread/sleep.c b/src/libthread/sleep.c
       t@@ -36,3 +36,4 @@ _threadwakeup(_Procrend *r)
                _threadready(t);
                unlock(&t->proc->lock);
        }
       +
 (DIR) diff --git a/src/libthread/thread.sh b/src/libthread/thread.sh
       t@@ -2,13 +2,13 @@
        
        if [ `uname` = Linux ]
        then
       -        case "`uname | awk '{print $3}'`" in
       -        *)
       -                echo Linux-clone
       -                ;;
       +        case `uname -r` in
                2.[6789]*)
                        echo pthread
                        ;;
       +        *)
       +                echo Linux-clone
       +                ;;
                esac
        else
                echo pthread
 (DIR) diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h
       t@@ -28,6 +28,14 @@ typedef struct Proc                Proc;
        typedef struct Tqueue        Tqueue;
        typedef struct Pqueue        Pqueue;
        typedef struct Execargs        Execargs;
       +typedef struct Jmp Jmp;
       +
       +/* sync with ../lib9/notify.c */
       +struct Jmp
       +{
       +        p9jmp_buf b;
       +};
       +
        
        typedef enum
        {
       t@@ -126,6 +134,7 @@ struct Proc
                Proc                *newproc;        /* fork argument */
                char                exitstr[ERRMAX];        /* exit status */
        
       +        int                internal;
                int                rforkflag;
                int                nthreads;
                Tqueue        threads;                /* All threads of this proc */
       t@@ -138,6 +147,7 @@ struct Proc
                uint                nextID;                /* ID of most recently created thread */
                Proc                *next;                /* linked list of Procs */
        
       +        Jmp                sigjmp;                /* for notify implementation */
        
                void                (*schedfn)(Proc*);        /* function to call in scheduler */
        
       t@@ -161,8 +171,6 @@ struct Proc
        void                _swaplabel(Label*, Label*);
        Proc*        _newproc(void);
        int                _newthread(Proc*, void(*)(void*), void*, uint, char*, int);
       -int                _procsplhi(void);
       -void                _procsplx(int);
        int                _sched(void);
        int                _schedexec(Execargs*);
        void                _schedexecwait(void);
       t@@ -178,14 +186,14 @@ void                _threadexitsall(char*);
        Proc*        _threadgetproc(void);
        extern void        _threadmultiproc(void);
        Proc*        _threaddelproc(void);
       -void                _threadinitproc(Proc*);
       -void                _threadwaitkids(Proc*);
       +void                _kthreadinitproc(Proc*);
        void                _threadsetproc(Proc*);
        void                _threadinitstack(Thread*, void(*)(void*), void*);
        void                _threadlinkmain(void);
        void*        _threadmalloc(long, int);
        void                _threadnote(void*, char*);
        void                _threadready(Thread*);
       +void                _threadschednote(void);
        void                _threadsetidle(int);
        void                _threadsleep(_Procrend*);
        void                _threadwakeup(_Procrend*);
       t@@ -195,12 +203,18 @@ long                _xdec(long*);
        void                _xinc(long*);
        void                _threadremove(Proc*, Thread*);
        void                threadstatus(void);
       -void                _threadstartproc(Proc*);
       -void                _threadexitproc(char*);
       -void                _threadexitallproc(char*);
        void                _threadefork(int[3], int[2], char*, char**);
       +Jmp*                _threadgetjmp(void);
       +void                _kthreadinit(void);
       +void                _kthreadsetproc(Proc*);
       +Proc*        _kthreadgetproc(void);
       +void                _kthreadstartproc(Proc*);
       +void                _kthreadexitproc(char*);
       +void                _kthreadexitallproc(char*);
       +void                _threadinternalproc(void);
       +void                _threadbackgroundinit(void);
       +void                _kmaininit(void);
        
       -extern int                        _threadmainpid;
        extern int                        _threadnprocs;
        extern int                        _threaddebuglevel;
        extern char*                _threadexitsallstatus;
       t@@ -222,8 +236,6 @@ extern void _threadstacklimit(void*, void*);
        extern void _procdelthread(Proc*, Thread*);
        extern void _procaddthread(Proc*, Thread*);
        
       -extern void _threadafterexec(void);
        extern void _threadmaininit(void);
       -extern void _threadfirstexec(void);
        extern int _threadexec(Channel*, int[3], char*, char*[], int);
       -extern int _callthreadexec(Channel*, int[3], char*, char*[], int);
       +extern int _kthreadexec(Channel*, int[3], char*, char*[], int);