aboutsummaryrefslogtreecommitdiff
path: root/src/libthread/execproc.ch
diff options
context:
space:
mode:
Diffstat (limited to 'src/libthread/execproc.ch')
-rw-r--r--src/libthread/execproc.ch183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/libthread/execproc.ch b/src/libthread/execproc.ch
new file mode 100644
index 00000000..4382be40
--- /dev/null
+++ b/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)
+{
+}
+