]> git.ozlabs.org Git - ccan/blob - ccan/antithread/antithread.c
9297cdac1d7f7b992e92e6284516df35778ff938
[ccan] / ccan / antithread / antithread.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <stdbool.h>
5 #include <string.h>
6 #include <sys/mman.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <errno.h>
10 #include <err.h>
11 #include "antithread.h"
12 #include <ccan/noerr/noerr.h>
13 #include <ccan/talloc/talloc.h>
14 #include <ccan/alloc/alloc.h>
15
16 /* FIXME: Valgrind support should be possible for some cases.  Tricky
17  * case is where another process allocates for you, but at worst we
18  * could reset what is valid and what isn't on every entry into the
19  * library or something. */
20
21 struct at_pool
22 {
23         const void *ctx;
24         void *pool;
25         unsigned long poolsize;
26         int fd;
27         int parent_rfd, parent_wfd;
28 };
29
30 struct athread
31 {
32         pid_t pid;
33         int rfd, wfd;
34 };
35
36 /* FIXME: Better locking through futexes. */
37 static void lock(int fd, unsigned long off)
38 {
39         struct flock fl;
40
41         fl.l_type = F_WRLCK;
42         fl.l_whence = SEEK_SET;
43         fl.l_start = off;
44         fl.l_len = 1;
45
46         while (fcntl(fd, F_SETLKW, &fl) < 0) {
47                 if (errno != EINTR)
48                         err(1, "Failure locking antithread file");
49         }
50 }
51
52 static void unlock(int fd, unsigned long off)
53 {
54         struct flock fl;
55         int serrno = errno;
56
57         fl.l_type = F_UNLCK;
58         fl.l_whence = SEEK_SET;
59         fl.l_start = off;
60         fl.l_len = 1;
61
62         fcntl(fd, F_SETLK, &fl);
63         errno = serrno;
64 }
65
66 static void *at_realloc(const void *parent, void *ptr, size_t size)
67 {
68         struct at_pool *p = talloc_find_parent_bytype(parent, struct at_pool);
69         /* FIXME: realloc in ccan/alloc? */
70         void *new;
71
72         lock(p->fd, 0);
73         if (size == 0) {
74                 alloc_free(p->pool, p->poolsize, ptr);
75                 new = NULL;
76         } else if (ptr == NULL) {
77                 /* FIXME: Alignment */
78                 new = alloc_get(p->pool, p->poolsize, size, 16);
79         } else {
80                 if (size <= alloc_size(p->pool, p->poolsize, ptr))
81                         new = ptr;
82                 else {
83                         new = alloc_get(p->pool, p->poolsize, size, 16);
84                         if (new) {
85                                 memcpy(new, ptr,
86                                        alloc_size(p->pool, p->poolsize, ptr));
87                                 alloc_free(p->pool, p->poolsize, ptr);
88                         }
89                 }
90         }
91         unlock(p->fd, 0);
92         return new;
93 }
94
95 /* We add 16MB to size.  This compensates for address randomization. */
96 #define PADDING (16 * 1024 * 1024)
97
98 /* Create a new sharable pool. */
99 struct at_pool *at_pool(unsigned long size)
100 {
101         int fd;
102         struct at_pool *p;
103         FILE *f;
104
105         /* FIXME: How much should we actually add for overhead?. */
106         size += 32 * getpagesize();
107
108         /* Round up to whole pages. */
109         size = (size + getpagesize()-1) & ~(getpagesize()-1);
110
111         f = tmpfile();
112         if (!f)
113                 return NULL;
114
115         fd = dup(fileno(f));
116         fclose_noerr(f);
117
118         if (fd < 0)
119                 return NULL;
120
121         if (ftruncate(fd, size + PADDING) != 0)
122                 goto fail_close;
123
124         p = talloc(NULL, struct at_pool);
125         if (!p)
126                 goto fail_close;
127
128         /* First map gets a nice big area. */
129         p->pool = mmap(NULL, size+PADDING, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
130                        0);
131         if (p->pool == MAP_FAILED)
132                 goto fail_free;
133
134         /* Then we remap into the middle of it. */
135         munmap(p->pool, size+PADDING);
136         p->pool = mmap(p->pool + PADDING/2, size, PROT_READ|PROT_WRITE,
137                        MAP_SHARED, fd, 0);
138         if (p->pool == MAP_FAILED)
139                 goto fail_free;
140
141         /* FIXME: Destructor? */
142         p->fd = fd;
143         p->poolsize = size;
144         p->parent_rfd = p->parent_wfd = -1;
145         alloc_init(p->pool, p->poolsize);
146
147         p->ctx = talloc_add_external(p, at_realloc);
148         if (!p->ctx)
149                 goto fail_unmap;
150
151         return p;
152
153 fail_unmap:
154         munmap(p->pool, size);
155 fail_free:
156         talloc_free(p);
157 fail_close:
158         close_noerr(fd);
159         return NULL;
160 }
161
162 /* Talloc off this to allocate from within the pool. */
163 const void *at_pool_ctx(struct at_pool *atp)
164 {
165         return atp->ctx;
166 }
167
168 static int cant_destroy_self(struct athread *at)
169 {
170         /* Perhaps this means we want to detach, but it doesn't really
171          * make sense. */
172         abort();
173         return 0;
174 }
175
176 static int destroy_at(struct athread *at)
177 {
178         /* If it is already a zombie, this is harmless. */
179         kill(at->pid, SIGTERM);
180
181         close(at->rfd);
182         close(at->wfd);
183
184         /* FIXME: Should we do SIGKILL if process doesn't exit soon? */
185         if (waitpid(at->pid, NULL, 0) != at->pid)
186                 err(1, "Waiting for athread %p (pid %u)", at, at->pid);
187
188         return 0;
189 }
190
191 /* Sets up thread and forks it.  NULL on error. */
192 static struct athread *fork_thread(struct at_pool *pool)
193 {
194         int p2c[2], c2p[2];
195         struct athread *at;
196
197         /* You can't already be a child of this pool. */
198         if (pool->parent_rfd != -1)
199                 errx(1, "Can't create antithread on this pool: we're one");
200
201         /* We don't want this allocated *in* the pool. */
202         at = talloc_steal(pool, talloc(NULL, struct athread));
203
204         if (pipe(p2c) != 0)
205                 goto free;
206
207         if (pipe(c2p) != 0)
208                 goto close_p2c;
209
210         at->pid = fork();
211         if (at->pid == -1)
212                 goto close_c2p;
213
214         if (at->pid == 0) {
215                 /* Child */
216                 close(c2p[0]);
217                 close(p2c[1]);
218                 pool->parent_rfd = p2c[0];
219                 pool->parent_wfd = c2p[1];
220                 talloc_set_destructor(at, cant_destroy_self);
221         } else {
222                 /* Parent */
223                 close(c2p[1]);
224                 close(p2c[0]);
225                 at->rfd = c2p[0];
226                 at->wfd = p2c[1];
227                 talloc_set_destructor(at, destroy_at);
228         }
229
230         return at;
231 close_c2p:
232         close_noerr(c2p[0]);
233         close_noerr(c2p[1]);
234 close_p2c:
235         close_noerr(p2c[0]);
236         close_noerr(p2c[1]);
237 free:
238         talloc_free(at);
239         return NULL;
240 }
241
242 /* Creating an antithread via fork() */
243 struct athread *_at_run(struct at_pool *pool,
244                         void *(*fn)(struct at_pool *, void *),
245                         void *obj)
246 {
247         struct athread *at;
248
249         at = fork_thread(pool);
250         if (!at)
251                 return NULL;
252
253         if (at->pid == 0) {
254                 /* Child */
255                 at_tell_parent(pool, fn(pool, obj));
256                 exit(0);
257         }
258         /* Parent */
259         return at;
260 }
261
262 static unsigned int num_args(char *const argv[])
263 {
264         unsigned int i;
265
266         for (i = 0; argv[i]; i++);
267         return i;
268 }
269
270 /* Fork and execvp, with added arguments for child to grab. */
271 struct athread *at_spawn(struct at_pool *pool, void *arg, char *cmdline[])
272 {
273         struct athread *at;
274         int err;
275
276         at = fork_thread(pool);
277         if (!at)
278                 return NULL;
279
280         if (at->pid == 0) {
281                 /* child */
282                 char *argv[num_args(cmdline) + 2];
283                 argv[0] = cmdline[0];
284                 argv[1] = talloc_asprintf(NULL, "AT:%p/%lu/%i/%i/%i/%p",
285                                           pool->pool, pool->poolsize,
286                                           pool->fd, pool->parent_rfd,
287                                           pool->parent_wfd, arg);
288                 /* Copy including NULL terminator. */
289                 memcpy(&argv[2], &cmdline[1], num_args(cmdline)*sizeof(char *));
290                 execvp(argv[0], argv);
291
292                 err = errno;
293                 write(pool->parent_wfd, &err, sizeof(err));
294                 exit(1);
295         }
296
297         /* Child should always write an error code (or 0). */
298         if (read(at->rfd, &err, sizeof(err)) != sizeof(err)) {
299                 errno = ECHILD;
300                 talloc_free(at);
301                 return NULL;
302         }
303         if (err != 0) {
304                 errno = err;
305                 talloc_free(at);
306                 return NULL;
307         }
308         return at;
309 }
310
311 /* The fd to poll on */
312 int at_fd(struct athread *at)
313 {
314         return at->rfd;
315 }
316
317 /* What's the antithread saying?  Blocks if fd not ready. */
318 void *at_read(struct athread *at)
319 {
320         void *ret;
321
322         switch (read(at->rfd, &ret, sizeof(ret))) {
323         case -1:
324                 err(1, "Reading from athread %p (pid %u)", at, at->pid);
325         case 0:
326                 /* Thread died. */
327                 return NULL;
328         case sizeof(ret):
329                 return ret;
330         default:
331                 /* Should never happen. */
332                 err(1, "Short read from athread %p (pid %u)", at, at->pid);
333         }
334 }
335
336 /* Say something to a child. */
337 void at_tell(struct athread *at, const void *status)
338 {
339         if (write(at->wfd, &status, sizeof(status)) != sizeof(status))
340                 err(1, "Failure writing to athread %p (pid %u)", at, at->pid);
341 }
342
343 /* For child to grab arguments from command line (removes them) */
344 struct at_pool *at_get_pool(int *argc, char *argv[], void **arg)
345 {
346         struct at_pool *p = talloc(NULL, struct at_pool);
347         void *map;
348         int err;
349
350         if (!argv[1]) {
351                 errno = EINVAL;
352                 goto fail;
353         }
354
355         /* If they don't care, use dummy value. */
356         if (arg == NULL)
357                 arg = &map;
358
359         if (sscanf(argv[1], "AT:%p/%lu/%i/%i/%i/%p", 
360                    &p->pool, &p->poolsize, &p->fd,
361                    &p->parent_rfd, &p->parent_wfd, arg) != 6) {
362                 errno = EINVAL;
363                 goto fail;
364         }
365
366         /* FIXME: To try to adjust for address space randomization, we
367          * could re-exec a few times. */
368         map = mmap(p->pool, p->poolsize, PROT_READ|PROT_WRITE, MAP_SHARED,
369                    p->fd, 0);
370         if (map != p->pool) {
371                 fprintf(stderr, "Mapping %lu bytes @%p gave %p\n",
372                         p->poolsize, p->pool, map);
373                 errno = ENOMEM;
374                 goto fail;
375         }
376
377         p->ctx = talloc_add_external(p, at_realloc);
378         if (!p->ctx)
379                 goto fail;
380
381         /* Tell parent we're good. */
382         err = 0;
383         if (write(p->parent_wfd, &err, sizeof(err)) != sizeof(err)) {
384                 errno = EBADF;
385                 goto fail;
386         }
387
388         /* Delete AT arg. */
389         memmove(&argv[1], &argv[2], --(*argc));
390         return p;
391
392 fail:
393         /* FIXME: cleanup properly. */
394         talloc_free(p);
395         return NULL;
396 }
397
398 /* Say something to our parent (async). */
399 void at_tell_parent(struct at_pool *pool, const void *status)
400 {
401         if (pool->parent_wfd == -1)
402                 errx(1, "This process is not an antithread of this pool");
403
404         if (write(pool->parent_wfd, &status, sizeof(status)) != sizeof(status))
405                 err(1, "Failure writing to parent");
406 }
407
408 /* What's the parent saying?  Blocks if fd not ready. */
409 void *at_read_parent(struct at_pool *pool)
410 {
411         void *ret;
412
413         if (pool->parent_rfd == -1)
414                 errx(1, "This process is not an antithread of this pool");
415
416         switch (read(pool->parent_rfd, &ret, sizeof(ret))) {
417         case -1:
418                 err(1, "Reading from parent");
419         case 0:
420                 /* Parent died. */
421                 return NULL;
422         case sizeof(ret):
423                 return ret;
424         default:
425                 /* Should never happen. */
426                 err(1, "Short read from parent");
427         }
428 }
429
430 /* The fd to poll on */
431 int at_parent_fd(struct at_pool *pool)
432 {
433         if (pool->parent_rfd == -1)
434                 errx(1, "This process is not an antithread of this pool");
435
436         return pool->parent_rfd;
437 }
438
439 /* FIXME: Futexme. */
440 void at_lock(void *obj)
441 {
442         struct at_pool *p = talloc_find_parent_bytype(obj, struct at_pool);
443 #if 0
444         unsigned int *l;
445
446         /* This isn't required yet, but ensures it's a talloc ptr */
447         l = talloc_lock_ptr(obj);
448 #endif
449
450         lock(p->fd, (char *)obj - (char *)p->pool);
451
452 #if 0
453         if (*l)
454                 errx(1, "Object %p was already locked (something died?)", obj);
455         *l = 1;
456 #endif
457 }
458
459 void at_unlock(void *obj)
460 {
461         struct at_pool *p = talloc_find_parent_bytype(obj, struct at_pool);
462 #if 0
463         unsigned int *l;
464
465         l = talloc_lock_ptr(obj);
466         if (!*l)
467                 errx(1, "Object %p was already unlocked", obj);
468         *l = 0;
469 #endif
470         unlock(p->fd, (char *)obj - (char *)p->pool);
471 }
472
473 void at_lock_all(struct at_pool *p)
474 {
475         lock(p->fd, 0);
476 }
477         
478 void at_unlock_all(struct at_pool *p)
479 {
480         unlock(p->fd, 0);
481 }