From 8d4a3514a8a8ba68c22be4c75fa58111fa5c2f9f Mon Sep 17 00:00:00 2001 From: rsc Date: Fri, 17 Sep 2004 22:11:21 +0000 Subject: was ffork-Linux.c. --- src/lib9/ffork-Linux-clone.c | 193 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/lib9/ffork-Linux-clone.c (limited to 'src/lib9/ffork-Linux-clone.c') diff --git a/src/lib9/ffork-Linux-clone.c b/src/lib9/ffork-Linux-clone.c new file mode 100644 index 00000000..f4704c60 --- /dev/null +++ b/src/lib9/ffork-Linux-clone.c @@ -0,0 +1,193 @@ +/* + * Is nothing simple? + * + * We can't free the stack until we've finished executing, + * but once we've finished executing, we can't do anything + * at all, including call free. So instead we keep a linked list + * of all stacks for all processes, and every few times we try + * to allocate a new stack we scan the current stack list for + * dead processes and reclaim those stacks. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "9proc.h" + +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) + if(kill(p->pid, 0) < 0 && errno == ESRCH){ + 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; +} + +static int +tramp(void *v) +{ + void (*fn)(void*), *arg; + void **v2; + void *p; + + _p9uproc(0); + v2 = v; + fn = v2[0]; + arg = v2[1]; + p = v2[2]; + free(v2); + fn(arg); + _exit(0); + return 0; +} + +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; +} + +int +ffork(int flags, void (*fn)(void*), void *arg) +{ + void **v; + char *p; + int cloneflag, pid, thepid, status, nowait; + + _p9uproc(0); + 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; +} + +int +getfforkid(void) +{ + return getpid(); +} + -- cgit v1.2.3