diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libthread/Linux-clone.c | 185 | ||||
-rw-r--r-- | src/libthread/pid.c | 25 | ||||
-rw-r--r-- | src/libthread/setproc.c | 36 | ||||
-rw-r--r-- | src/libthread/sysofiles.sh | 11 | ||||
-rw-r--r-- | src/libthread/tsignal.c | 43 |
5 files changed, 300 insertions, 0 deletions
diff --git a/src/libthread/Linux-clone.c b/src/libthread/Linux-clone.c new file mode 100644 index 00000000..1f5e3bde --- /dev/null +++ b/src/libthread/Linux-clone.c @@ -0,0 +1,185 @@ +#include <u.h> +#include <errno.h> +#include <sched.h> +#include <sys/signal.h> +#include <sys/wait.h> +#include "threadimpl.h" + +#define procid() getpid() +#define procexited(id) (kill((id), 0) < 0 && errno==ESRCH) + +static int multi; +static Proc *theproc; + +/* + * Run all execs forked from a special exec proc. + */ +#include "execproc.ch" + +/* + * Use _exits to exit one proc, and signals to tear everyone else down. + */ +#include "exit-getpid.ch" + +/* + * Use table for _threadmultiproc, _threadsetproc, _threadgetproc. + */ +#include "proctab.ch" + +/* + * Use per-process stack allocation code. + */ +#include "procstack.ch" + +/* + * Implement _threadstartproc using clone. + * + * Cannot use this on newer kernels (2.6+) because + * on those kernels clone allows you to set up a per-thread + * segment using %gs, and the C library and compilers + * assume that you've done this. I don't want to learn + * how to do this (the code below is scary enough), + * so on those more recent kernels we use nptl (the + * pthreads implementation), which is finally good enough. + */ + +/* + * Trampoline to run f(arg). + */ +static int +tramp(void *v) +{ + void (*fn)(void*), *arg; + void **v2; + void *p; + + v2 = v; + fn = v2[0]; + arg = v2[1]; + p = v2[2]; + free(v2); + fn(arg); + abort(); /* not reached! */ + return 0; +} + +/* + * Trampnowait runs in the child, and starts a granchild to run f(arg). + * When trampnowait proc exits, the connection between the + * grandchild running f(arg) and the parent/grandparent is lost, so the + * grandparent need not worry about cleaning up a zombie + * when the grandchild finally exits. + */ +static int +trampnowait(void *v) +{ + int pid; + int cloneflag; + void **v2; + int *pidp; + void *p; + + v2 = v; + cloneflag = (int)v2[4]; + pidp = v2[3]; + p = v2[2]; + pid = clone(tramp, p+fforkstacksize-512, cloneflag, v); + *pidp = pid; + _exit(0); + return 0; +} + +static int +ffork(int flags, void (*fn)(void*), void *arg) +{ + void **v; + char *p; + int cloneflag, pid, thepid, status, nowait; + + p = mallocstack(); + v = malloc(sizeof(void*)*5); + if(p==nil || v==nil){ + freestack(p); + free(v); + return -1; + } + cloneflag = 0; + flags &= ~RFPROC; + if(flags&RFMEM){ + cloneflag |= CLONE_VM; + flags &= ~RFMEM; + } + if(!(flags&RFFDG)) + cloneflag |= CLONE_FILES; + else + flags &= ~RFFDG; + nowait = flags&RFNOWAIT; +// if(!(flags&RFNOWAIT)) +// cloneflag |= SIGCHLD; +// else + flags &= ~RFNOWAIT; + if(flags){ + fprint(2, "unknown rfork flags %x\n", flags); + freestack(p); + free(v); + return -1; + } + v[0] = fn; + v[1] = arg; + v[2] = p; + v[3] = &thepid; + v[4] = (void*)cloneflag; + thepid = -1; + pid = clone(nowait ? trampnowait : tramp, p+fforkstacksize-16, cloneflag, v); + if(pid > 0 && nowait){ + if(wait4(pid, &status, __WALL, 0) < 0) + fprint(2, "ffork wait4: %r\n"); + }else + thepid = pid; + if(thepid == -1) + freestack(p); + else{ + ((Stack*)p)->pid = thepid; + } + return thepid; +} + +/* + * Called to start a new proc. + */ +void +_threadstartproc(Proc *p) +{ + int pid; + Proc *np; + + np = p->newproc; + pid = ffork(RFPROC|RFMEM|RFNOWAIT, _threadscheduler, np); + if(pid == -1) + sysfatal("starting new proc: %r"); + np->pid = pid; +} + +/* + * Called to associate p with the current pthread. + */ +void +_threadinitproc(Proc *p) +{ + sigset_t mask; + + p->pid = getpid(); + sigemptyset(&mask); + sigaddset(&mask, WAITSIG); + sigprocmask(SIG_BLOCK, &mask, nil); + _threadsetproc(p); +} + +/* + * Called from mainlauncher before threadmain. + */ +void +_threadmaininit(void) +{ +} + diff --git a/src/libthread/pid.c b/src/libthread/pid.c new file mode 100644 index 00000000..bbc7dbbf --- /dev/null +++ b/src/libthread/pid.c @@ -0,0 +1,25 @@ + mypid = getpid(); + + /* + * signal others. + * copying all the pids first avoids other thread's + * teardown procedures getting in the way. + */ + lock(&_threadpq.lock); + npid = 0; + for(p=_threadpq.head; p; p=p->next) + npid++; + pid = _threadmalloc(npid*sizeof(pid[0]), 0); + npid = 0; + for(p = _threadpq.head; p; p=p->next) + pid[npid++] = p->pid; + unlock(&_threadpq.lock); + for(i=0; i<npid; i++){ + _threaddebug(DBGSCHED, "threadexitsall kill %d", pid[i]); + if(pid[i]==0 || pid[i]==-1) + fprint(2, "bad pid in threadexitsall: %d\n", pid[i]); + else if(pid[i] != mypid){ + kill(pid[i], SIGTERM); + } + } + diff --git a/src/libthread/setproc.c b/src/libthread/setproc.c new file mode 100644 index 00000000..5f1e2a8d --- /dev/null +++ b/src/libthread/setproc.c @@ -0,0 +1,36 @@ +/* + * Avoid using threading calls for single-proc programs. + */ + +#include "threadimpl.h" + +static int multi; +static Proc *theproc; + +void +_threadsetproc(Proc *p) +{ + if(!multi) + theproc = p; + else + _kthreadsetproc(p); +} + +Proc* +_threadgetproc(void) +{ + if(!multi) + return theproc; + return _kthreadgetproc(); +} + +void +_threadmultiproc(void) +{ + if(multi) + return; + + multi = 1; + _kthreadinit(); + _threadsetproc(theproc); +} diff --git a/src/libthread/sysofiles.sh b/src/libthread/sysofiles.sh new file mode 100644 index 00000000..11b21814 --- /dev/null +++ b/src/libthread/sysofiles.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +case "`uname`-`uname -r`" in +Linux-2.[01234]*) + echo Linux-clone.o ucontext.o + exit 0 + ;; +esac + +echo pthread.o ucontext.o +exit 0 diff --git a/src/libthread/tsignal.c b/src/libthread/tsignal.c new file mode 100644 index 00000000..9c64c46b --- /dev/null +++ b/src/libthread/tsignal.c @@ -0,0 +1,43 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> + +extern int _threaddebuglevel; + +void +usage(void) +{ + fprint(2, "usage: tsignal [-[ednf] note]*\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char **argv) +{ + Channel *c; + char *msg; + + ARGBEGIN{ + case 'D': + _threaddebuglevel = ~0; + break; + default: + usage(); + case 'e': + notifyenable(EARGF(usage())); + break; + case 'd': + notifydisable(EARGF(usage())); + break; + case 'n': + notifyon(EARGF(usage())); + break; + case 'f': + notifyoff(EARGF(usage())); + break; + }ARGEND + + c = threadnotechan(); + while((msg = recvp(c)) != nil) + print("note: %s\n", msg); +} |