Commit Diff


commit - cf4f3eafc6016ccdb57773215dcdd5ebac95c07d
commit + 24f4e66b12cf0501d0f239ade10b2963f46e6755
blob - /dev/null
blob + 1f5e3bdeb7d436f0844d6940dcd8df8706ed26ae (mode 644)
--- /dev/null
+++ 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)
+{
+}
+
blob - /dev/null
blob + bbc7dbbfb4d26443b024cd820345ee8445068b8f (mode 644)
--- /dev/null
+++ 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);
+		}
+	}
+
blob - /dev/null
blob + 5f1e2a8d0bffafc72f0e57357f860dc43f97058e (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 11b218145a2361146d8dc1e4c54a83955435aac7 (mode 644)
--- /dev/null
+++ 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
blob - /dev/null
blob + 9c64c46bd39eb5ee3d1dfe5dc8c9fc5d58742cdb (mode 644)
--- /dev/null
+++ 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);
+}