aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libthread/Linux-clone.c185
-rw-r--r--src/libthread/pid.c25
-rw-r--r--src/libthread/setproc.c36
-rw-r--r--src/libthread/sysofiles.sh11
-rw-r--r--src/libthread/tsignal.c43
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);
+}