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 anything
6 * at all, including call free. So instead we keep a linked list
7 * of all stacks for all processes, and every few times we try
8 * to allocate a new stack we scan the current stack list for
9 * 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 Stack
25 {
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 void
63 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 void
81 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 int
97 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;
114 static int
115 trampnowait(void *v)
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;
133 int
134 ffork(int flags, void (*fn)(void*), void *arg)
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;
148 cloneflag = 0;
149 flags &= ~RFPROC;
150 if(flags&RFMEM){
151 cloneflag |= CLONE_VM;
152 flags &= ~RFMEM;
154 if(!(flags&RFFDG))
155 cloneflag |= CLONE_FILES;
156 else
157 flags &= ~RFFDG;
158 nowait = flags&RFNOWAIT;
159 if(!(flags&RFNOWAIT))
160 cloneflag |= SIGCHLD;
161 else
162 flags &= ~RFNOWAIT;
163 if(flags){
164 fprint(2, "unknown rfork flags %x\n", flags);
165 freestack(p);
166 free(v);
167 return -1;
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 }else
180 thepid = pid;
181 if(thepid == -1)
182 freestack(p);
183 else
184 ((Stack*)p)->pid = thepid;
185 return thepid;
188 int
189 getfforkid(void)
191 return getpid();