Blob
1 /*2 * Is nothing simple?3 *4 * We can't free the stack until we've finished executing,5 * but once we've finished executing, we can't do anything6 * at all, including call free. So instead we keep a linked list7 * of all stacks for all processes, and every few times we try8 * to allocate a new stack we scan the current stack list for9 * dead processes and reclaim those stacks.10 */12 #include <u.h>13 #include <sys/types.h>14 #include <sys/wait.h>15 #include <sched.h>16 #include <signal.h>17 #include <errno.h>18 #include <libc.h>19 #include "9proc.h"21 int fforkstacksize = 16384;23 typedef struct Stack Stack;24 struct Stack25 {26 Stack *next;27 Stack *fnext;28 int pid;29 };31 static Lock stacklock;32 static Stack *freestacks;33 static Stack *allstacks;34 static int stackmallocs;35 static void gc(void);37 static void*38 mallocstack(void)39 {40 Stack *p;42 lock(&stacklock);43 top:44 p = freestacks;45 if(p)46 freestacks = p->fnext;47 else{48 if(stackmallocs++%1 == 0)49 gc();50 if(freestacks)51 goto top;52 p = malloc(fforkstacksize);53 p->next = allstacks;54 allstacks = p;55 }56 if(p)57 p->pid = 1;58 unlock(&stacklock);59 return p;60 }62 static void63 gc(void)64 {65 Stack *p;67 for(p=allstacks; p; p=p->next){68 if(p->pid > 1)69 if(kill(p->pid, 0) < 0 && errno == ESRCH){70 if(0) fprint(2, "reclaim stack from %d\n", p->pid);71 p->pid = 0;72 }73 if(p->pid == 0){74 p->fnext = freestacks;75 freestacks = p;76 }77 }78 }80 static void81 freestack(void *v)82 {83 Stack *p;85 p = v;86 if(p == nil)87 return;88 lock(&stacklock);89 p->fnext = freestacks;90 p->pid = 0;91 freestacks = p;92 unlock(&stacklock);93 return;94 }96 static int97 tramp(void *v)98 {99 void (*fn)(void*), *arg;100 void **v2;101 void *p;103 _p9uproc(0);104 v2 = v;105 fn = v2[0];106 arg = v2[1];107 p = v2[2];108 free(v2);109 fn(arg);110 _exit(0);111 return 0;112 }114 static int115 trampnowait(void *v)116 {117 int pid;118 int cloneflag;119 void **v2;120 int *pidp;121 void *p;123 v2 = v;124 cloneflag = (int)v2[4];125 pidp = v2[3];126 p = v2[2];127 pid = clone(tramp, p+fforkstacksize-512, cloneflag, v);128 *pidp = pid;129 _exit(0);130 return 0;131 }133 int134 ffork(int flags, void (*fn)(void*), void *arg)135 {136 void **v;137 char *p;138 int cloneflag, pid, thepid, status, nowait;140 _p9uproc(0);141 p = mallocstack();142 v = malloc(sizeof(void*)*5);143 if(p==nil || v==nil){144 freestack(p);145 free(v);146 return -1;147 }148 cloneflag = 0;149 flags &= ~RFPROC;150 if(flags&RFMEM){151 cloneflag |= CLONE_VM;152 flags &= ~RFMEM;153 }154 if(!(flags&RFFDG))155 cloneflag |= CLONE_FILES;156 else157 flags &= ~RFFDG;158 nowait = flags&RFNOWAIT;159 if(!(flags&RFNOWAIT))160 cloneflag |= SIGCHLD;161 else162 flags &= ~RFNOWAIT;163 if(flags){164 fprint(2, "unknown rfork flags %x\n", flags);165 freestack(p);166 free(v);167 return -1;168 }169 v[0] = fn;170 v[1] = arg;171 v[2] = p;172 v[3] = &thepid;173 v[4] = (void*)cloneflag;174 thepid = -1;175 pid = clone(nowait ? trampnowait : tramp, p+fforkstacksize-16, cloneflag, v);176 if(pid > 0 && nowait){177 if(wait4(pid, &status, __WALL, 0) < 0)178 fprint(2, "ffork wait4: %r\n");179 }else180 thepid = pid;181 if(thepid == -1)182 freestack(p);183 else184 ((Stack*)p)->pid = thepid;185 return thepid;186 }188 int189 getfforkid(void)190 {191 return getpid();192 }