URI: 
       textra files - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit cf4f3eafc6016ccdb57773215dcdd5ebac95c07d
   DIR parent 195645536743aeb99eb336726823c38716cec02d
  HTML Author: rsc <devnull@localhost>
       Date:   Mon,  8 Nov 2004 16:03:42 +0000
       
       extra files
       
       Diffstat:
         A src/libthread/execproc.ch           |     183 +++++++++++++++++++++++++++++++
         A src/libthread/exit-getpid.ch        |      25 +++++++++++++++++++++++++
         A src/libthread/procstack.ch          |      75 +++++++++++++++++++++++++++++++
         A src/libthread/proctab.ch            |      97 ++++++++++++++++++++++++++++++
       
       4 files changed, 380 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/src/libthread/execproc.ch b/src/libthread/execproc.ch
       t@@ -0,0 +1,183 @@
       +/*
       + * Set up a dedicated proc to handle calls to exec.
       + * The proc also waits for child messages.  
       + * This way, each proc scheduler need not worry
       + * about calling wait in its main loop.
       + * 
       + * To be included from other files (e.g., Linux-clone.c).
       + */
       +
       +typedef struct Xarg Xarg;
       +struct Xarg
       +{
       +        Channel *pidc;
       +        int fd[3];
       +        char *prog;
       +        char **args;
       +        int freeargs;
       +        Channel *ret;
       +        int errn;
       +        char errstr[ERRMAX];
       +};
       +
       +static Proc *_threadexecproc;
       +static Channel *_threadexecchan;
       +static Lock threadexeclock;
       +
       +/*
       + * Called to poll for any kids of this pthread.
       + * We have a separate proc responsible for exec,
       + * so this is a no-op.
       + */
       +void
       +_threadwaitkids(Proc *p)
       +{
       +}
       +
       +#define WAITSIG SIGCHLD
       +
       +/*
       + * Separate process to wait for child messages.
       + * Also runs signal handlers and runs all execs.
       + */
       +static void
       +nop(int sig)
       +{
       +        USED(sig);
       +}
       +
       +static void
       +_threadwaitproc(void *v)
       +{
       +        Channel *c;
       +        Waitmsg *w;
       +        sigset_t mask;
       +        int ret, nkids;
       +        Xarg *xa;
       +
       +        nkids = 0;
       +
       +        sigemptyset(&mask);
       +        siginterrupt(WAITSIG, 1);
       +        signal(WAITSIG, nop);
       +        sigaddset(&mask, WAITSIG);
       +        sigprocmask(SIG_BLOCK, &mask, nil);
       +        USED(v);
       +        for(;;){
       +                while((nkids > 0 ? nbrecv : recv)(_threadexecchan, &xa) == 1){
       +                        ret = _threadexec(xa->pidc, xa->fd, xa->prog, xa->args, xa->freeargs);
       +                        if(ret > 0)
       +                                nkids++;
       +                        else{
       +                                rerrstr(xa->errstr, sizeof xa->errstr);
       +                                xa->errn = errno;
       +                        }
       +                        sendul(xa->ret, ret);
       +                }
       +                if(nkids > 0){
       +                        sigprocmask(SIG_UNBLOCK, &mask, nil);
       +                        w = wait();
       +                        sigprocmask(SIG_BLOCK, &mask, nil);
       +                        if(w == nil && errno == ECHILD){
       +                                fprint(2, "wait returned ECHILD but nkids=%d; reset\n", nkids);
       +                                nkids = 0;
       +                        }
       +                        if(w){
       +                                nkids--;
       +                                if((c = _threadwaitchan) != nil)
       +                                        sendp(c, w);
       +                                else
       +                                        free(w);
       +                        }
       +                }
       +        }
       +}
       +
       +static void _kickexecproc(void);
       +
       +int
       +_callthreadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
       +{
       +        int ret;
       +        Xarg xa;
       +
       +        if(_threadexecchan == nil){
       +                lock(&threadexeclock);
       +                if(_threadexecchan == nil)
       +                        _threadfirstexec();
       +                unlock(&threadexeclock);
       +        }
       +
       +        xa.pidc = pidc;
       +        xa.fd[0] = fd[0];
       +        xa.fd[1] = fd[1];
       +        xa.fd[2] = fd[2];
       +        xa.prog = prog;
       +        xa.args = args;
       +        xa.freeargs = freeargs;
       +        xa.ret = chancreate(sizeof(ulong), 1);
       +        sendp(_threadexecchan, &xa);
       +        _kickexecproc();
       +        ret = recvul(xa.ret);
       +        if(ret < 0){
       +                werrstr("%s", xa.errstr);
       +                errno = xa.errn;
       +        }
       +        chanfree(xa.ret);
       +        return ret;
       +}
       +
       +/* 
       + * Called before the first exec.
       + */
       +void
       +_threadfirstexec(void)
       +{
       +        int id;
       +        Proc *p;
       +
       +        _threadexecchan = chancreate(sizeof(Xarg*), 1);
       +        id = proccreate(_threadwaitproc, nil, 32*1024);
       +
       +        /*
       +         * Sleazy: decrement threadnprocs so that 
       +         * the existence of the _threadwaitproc proc
       +         * doesn't keep us from exiting.
       +         */
       +        lock(&_threadpq.lock);
       +        --_threadnprocs;
       +        for(p=_threadpq.head; p; p=p->next)
       +                if(p->threads.head && p->threads.head->id == id)
       +                        break;
       +        if(p == nil)
       +                sysfatal("cannot find exec proc");
       +        unlock(&_threadpq.lock);
       +        _threadexecproc = p;
       +}
       +
       +/*
       + * Called after the thread t has been rescheduled.
       + * Kick the exec proc in case it is in the middle of a wait.
       + */
       +static void
       +_kickexecproc(void)
       +{
       +        kill(_threadexecproc->pid, WAITSIG);
       +}
       +
       +/*
       + * Called before exec.
       + */
       +void
       +_threadbeforeexec(void)
       +{
       +}
       +
       +/*
       + * Called after exec.
       + */
       +void
       +_threadafterexec(void)
       +{
       +}
       +
   DIR diff --git a/src/libthread/exit-getpid.ch b/src/libthread/exit-getpid.ch
       t@@ -0,0 +1,25 @@
       +/*
       + * Implement threadexitsall by sending a signal to every proc.
       + *
       + * To be included from another C file (e.g., Linux-clone.c).
       + */
       +
       +void
       +_threadexitallproc(char *exitstr)
       +{
       +        Proc *p;
       +        int mypid;
       +
       +        mypid = getpid();
       +        lock(&_threadpq.lock);
       +        for(p=_threadpq.head; p; p=p->next)
       +                if(p->pid > 1 && p->pid != mypid)
       +                        kill(p->pid, SIGUSR2);
       +        exits(exitstr);
       +}
       +
       +void
       +_threadexitproc(char *exitstr)
       +{
       +        _exits(exitstr);
       +}
   DIR diff --git a/src/libthread/procstack.ch b/src/libthread/procstack.ch
       t@@ -0,0 +1,75 @@
       +static int fforkstacksize = 16384;
       +
       +typedef struct Stack Stack;
       +struct Stack
       +{
       +        Stack *next;
       +        Stack *fnext;
       +        int pid;
       +};
       +
       +static Lock stacklock;
       +static Stack *freestacks;
       +static Stack *allstacks;
       +static int stackmallocs;
       +static void gc(void);
       +
       +static void*
       +mallocstack(void)
       +{
       +        Stack *p;
       +
       +        lock(&stacklock);
       +top:
       +        p = freestacks;
       +        if(p)
       +                freestacks = p->fnext;
       +        else{
       +                if(stackmallocs++%1 == 0)
       +                        gc();
       +                if(freestacks)
       +                        goto top;
       +                p = malloc(fforkstacksize);
       +                p->next = allstacks;
       +                allstacks = p;
       +        }
       +        if(p)
       +                p->pid = 1;
       +        unlock(&stacklock);
       +        return p;
       +}
       +
       +static void
       +gc(void)
       +{
       +        Stack *p;
       +
       +        for(p=allstacks; p; p=p->next){
       +                if(p->pid > 1 && procexited(p->pid)){
       +                        if(0) fprint(2, "reclaim stack from %d\n", p->pid);
       +                        p->pid = 0;
       +                }
       +                if(p->pid == 0){
       +                        p->fnext = freestacks;
       +                        freestacks = p;
       +                }
       +        }
       +}
       +
       +static void
       +freestack(void *v)
       +{
       +        Stack *p;
       +
       +        p = v;
       +        if(p == nil)
       +                return;
       +        lock(&stacklock);
       +        p->fnext = freestacks;
       +        p->pid = 0;
       +        freestacks = p;
       +        unlock(&stacklock);
       +        return;
       +}
       +
       +
   DIR diff --git a/src/libthread/proctab.ch b/src/libthread/proctab.ch
       t@@ -0,0 +1,97 @@
       +/*
       + * Proc structure hash table indexed by proctabid() (usually getpid()).
       + * No lock is necessary for lookups (important when called from signal
       + * handlers).
       + * 
       + * To be included from other files (e.g., Linux-clone.c).
       + */
       +
       +#define T ((void*)-1)
       +
       +enum
       +{
       +        PTABHASH = 1031,
       +};
       +
       +static Lock ptablock;
       +static Proc *proctab[PTABHASH];
       +static Proc *theproc;
       +static int multi;
       +
       +void
       +_threadmultiproc(void)
       +{
       +        if(multi == 0){
       +                multi = 1;
       +                _threadsetproc(theproc);
       +        }
       +}
       +
       +void
       +_threadsetproc(Proc *p)
       +{
       +        int i, h;
       +        Proc **t;
       +
       +        if(!multi){
       +                theproc = p;
       +                return;
       +        }
       +        lock(&ptablock);
       +        p->procid = procid();
       +        h = p->procid%PTABHASH;
       +        for(i=0; i<PTABHASH; i++){
       +                t = &proctab[(h+i)%PTABHASH];
       +                if(*t==nil || *t==T){
       +                        *t = p;
       +                        break;
       +                }
       +        }
       +        unlock(&ptablock);
       +        if(i == PTABHASH)
       +                sysfatal("too many procs - proctab is full");
       +}
       +
       +static Proc**
       +_threadfindproc(int id)
       +{
       +        int i, h;
       +        Proc **t;
       +
       +        if(!multi)
       +                return &theproc;
       +
       +        h = id%PTABHASH;
       +        for(i=0; i<PTABHASH; i++){
       +                t = &proctab[(h+i)%PTABHASH];
       +                if(*t != nil && *t != T && (*t)->procid == id){
       +                        unlock(&ptablock);
       +                        return t;
       +                }
       +        }
       +        return nil;
       +}
       +
       +Proc*
       +_threadgetproc(void)
       +{
       +        Proc **t;
       +
       +        t = _threadfindproc(procid());
       +        if(t == nil)
       +                return nil;
       +        return *t;
       +}
       +
       +Proc*
       +_threaddelproc(void)
       +{
       +        Proc **t, *p;
       +
       +        t = _threadfindproc(procid());
       +        if(t == nil)
       +                return nil;
       +        p = *t;
       +        *t = T;
       +        return p;
       +}