Commit Diff


commit - 195645536743aeb99eb336726823c38716cec02d
commit + cf4f3eafc6016ccdb57773215dcdd5ebac95c07d
blob - /dev/null
blob + 4382be402347886592df25c8aafa044ed52a2f42 (mode 644)
--- /dev/null
+++ src/libthread/execproc.ch
@@ -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)
+{
+}
+
blob - /dev/null
blob + e2580ac77e38f8460b3bc68cbed606a149076935 (mode 644)
--- /dev/null
+++ src/libthread/exit-getpid.ch
@@ -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);
+}
blob - /dev/null
blob + ccf81866cec7b4ab9ef08e695cc7a19b39c53331 (mode 644)
--- /dev/null
+++ src/libthread/procstack.ch
@@ -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;
+}
+
+
blob - /dev/null
blob + d51543f991482d954b167ec681c8ac0a24264a38 (mode 644)
--- /dev/null
+++ src/libthread/proctab.ch
@@ -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;
+}