blob: 1f5e3bdeb7d436f0844d6940dcd8df8706ed26ae [file] [log] [blame]
#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)
{
}