#include #include #include #include #include #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) { }