13 #include "antithread.h"
14 #include <ccan/noerr/noerr.h>
15 #include <ccan/talloc/talloc.h>
16 #include <ccan/read_write_all/read_write_all.h>
17 #include <ccan/alloc/alloc.h>
18 #include <ccan/list/list.h>
20 /* FIXME: Valgrind support should be possible for some cases. Tricky
21 * case is where another process allocates for you, but at worst we
22 * could reset what is valid and what isn't on every entry into the
23 * library or something. */
25 static LIST_HEAD(pools);
27 /* Talloc destroys parents before children (damn Tridge's failing destructors!)
28 * so we need the first child (ie. last-destroyed) to actually clean up. */
29 struct at_pool_contents {
30 struct list_node list;
32 unsigned long poolsize;
34 int parent_rfd, parent_wfd;
39 struct at_pool_contents *p;
48 /* FIXME: Better locking through futexes. */
49 static void lock(int fd, unsigned long off)
54 fl.l_whence = SEEK_SET;
58 while (fcntl(fd, F_SETLKW, &fl) < 0) {
60 err(1, "Failure locking antithread file");
64 static void unlock(int fd, unsigned long off)
70 fl.l_whence = SEEK_SET;
74 fcntl(fd, F_SETLK, &fl);
78 /* This pointer is in a pool. Find which one. */
79 static struct at_pool_contents *find_pool(const void *ptr)
81 struct at_pool_contents *p;
83 list_for_each(&pools, p, list) {
84 /* Special case for initial allocation: ptr *is* pool */
88 if ((char *)ptr >= (char *)p->pool
89 && (char *)ptr < (char *)p->pool + p->poolsize)
95 static int destroy_pool(struct at_pool_contents *p)
98 munmap(p->pool, p->poolsize);
100 close(p->parent_rfd);
101 close(p->parent_wfd);
105 static void *at_realloc(const void *parent, void *ptr, size_t size)
107 struct at_pool_contents *p = find_pool(parent);
108 /* FIXME: realloc in ccan/alloc? */
112 alloc_free(p->pool, p->poolsize, ptr);
114 } else if (ptr == NULL) {
115 /* FIXME: Alignment */
116 new = alloc_get(p->pool, p->poolsize, size, 16);
118 if (size <= alloc_size(p->pool, p->poolsize, ptr))
121 new = alloc_get(p->pool, p->poolsize, size, 16);
124 alloc_size(p->pool, p->poolsize, ptr));
125 alloc_free(p->pool, p->poolsize, ptr);
133 static struct at_pool_contents *locked;
134 static void talloc_lock(const void *ptr)
136 struct at_pool_contents *p = find_pool(ptr);
143 static void talloc_unlock(void)
145 struct at_pool_contents *p = locked;
151 /* We add 16MB to size. This compensates for address randomization. */
152 #define PADDING (16 * 1024 * 1024)
154 /* Create a new sharable pool. */
155 struct at_pool *at_pool(unsigned long size)
159 struct at_pool_contents *p;
162 /* FIXME: How much should we actually add for overhead?. */
163 size += 32 * getpagesize();
165 /* Round up to whole pages. */
166 size = (size + getpagesize()-1) & ~(getpagesize()-1);
178 if (ftruncate(fd, size + PADDING) != 0)
181 atp = talloc(NULL, struct at_pool);
185 atp->p = p = talloc(NULL, struct at_pool_contents);
189 /* First map gets a nice big area. */
190 p->pool = mmap(NULL, size+PADDING, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
192 if (p->pool == MAP_FAILED)
195 /* Then we remap into the middle of it. */
196 munmap(p->pool, size+PADDING);
197 p->pool = mmap((char *)p->pool + PADDING/2, size, PROT_READ|PROT_WRITE,
199 if (p->pool == MAP_FAILED)
204 p->parent_rfd = p->parent_wfd = -1;
206 alloc_init(p->pool, p->poolsize);
207 list_add(&pools, &p->list);
208 talloc_set_destructor(p, destroy_pool);
210 atp->ctx = talloc_add_external(atp,
211 at_realloc, talloc_lock, talloc_unlock);
223 /* Talloc off this to allocate from within the pool. */
224 const void *at_pool_ctx(struct at_pool *atp)
229 static int cant_destroy_self(struct athread *at)
231 /* Perhaps this means we want to detach, but it doesn't really
237 static int destroy_at(struct athread *at)
239 /* If it is already a zombie, this is harmless. */
240 kill(at->pid, SIGTERM);
245 /* FIXME: Should we do SIGKILL if process doesn't exit soon? */
246 if (waitpid(at->pid, NULL, 0) != at->pid)
247 err(1, "Waiting for athread %p (pid %u)", at, at->pid);
252 /* Sets up thread and forks it. NULL on error. */
253 static struct athread *fork_thread(struct at_pool *atp)
257 struct at_pool_contents *pool = atp->p;
259 /* You can't already be a child of this pool. */
260 if (pool->parent_rfd != -1)
261 errx(1, "Can't create antithread on this pool: we're one");
263 /* We don't want this allocated *in* the pool. */
264 at = talloc_steal(atp, talloc(NULL, struct athread));
280 pool->parent_rfd = p2c[0];
281 pool->parent_wfd = c2p[1];
282 talloc_set_destructor(at, cant_destroy_self);
289 talloc_set_destructor(at, destroy_at);
304 /* Creating an antithread via fork() */
305 struct athread *_at_run(struct at_pool *atp,
306 void *(*fn)(struct at_pool *, void *),
311 at = fork_thread(atp);
317 at_tell_parent(atp, fn(atp, obj));
324 static unsigned int num_args(char *const argv[])
328 for (i = 0; argv[i]; i++);
332 /* Fork and execvp, with added arguments for child to grab. */
333 struct athread *at_spawn(struct at_pool *atp, void *arg, char *cmdline[])
338 at = fork_thread(atp);
344 char *argv[num_args(cmdline) + 2];
345 argv[0] = cmdline[0];
346 argv[1] = talloc_asprintf(NULL, "AT:%p/%lu/%i/%i/%i/%p",
347 atp->p->pool, atp->p->poolsize,
348 atp->p->fd, atp->p->parent_rfd,
349 atp->p->parent_wfd, arg);
350 /* Copy including NULL terminator. */
351 memcpy(&argv[2], &cmdline[1], num_args(cmdline)*sizeof(char *));
352 execvp(argv[0], argv);
355 write_all(atp->p->parent_wfd, &err, sizeof(err));
359 /* Child should always write an error code (or 0). */
360 if (read(at->rfd, &err, sizeof(err)) != sizeof(err)) {
373 /* The fd to poll on */
374 int at_fd(struct athread *at)
379 /* What's the antithread saying? Blocks if fd not ready. */
380 void *at_read(struct athread *at)
384 switch (read(at->rfd, &ret, sizeof(ret))) {
386 err(1, "Reading from athread %p (pid %u)", at, at->pid);
393 /* Should never happen. */
394 err(1, "Short read from athread %p (pid %u)", at, at->pid);
398 /* Say something to a child. */
399 void at_tell(struct athread *at, const void *status)
401 if (write(at->wfd, &status, sizeof(status)) != sizeof(status))
402 err(1, "Failure writing to athread %p (pid %u)", at, at->pid);
405 /* For child to grab arguments from command line (removes them) */
406 struct at_pool *at_get_pool(int *argc, char *argv[], void **arg)
408 struct at_pool *atp = talloc(NULL, struct at_pool);
409 struct at_pool_contents *p;
418 /* If they don't care, use dummy value. */
422 p = atp->p = talloc(atp, struct at_pool_contents);
424 if (sscanf(argv[1], "AT:%p/%lu/%i/%i/%i/%p",
425 &p->pool, &p->poolsize, &p->fd,
426 &p->parent_rfd, &p->parent_wfd, arg) != 6) {
431 /* FIXME: To try to adjust for address space randomization, we
432 * could re-exec a few times. */
433 map = mmap(p->pool, p->poolsize, PROT_READ|PROT_WRITE, MAP_SHARED,
435 if (map != p->pool) {
436 fprintf(stderr, "Mapping %lu bytes @%p gave %p\n",
437 p->poolsize, p->pool, map);
442 list_add(&pools, &p->list);
443 talloc_set_destructor(p, destroy_pool);
446 atp->ctx = talloc_add_external(atp,
447 at_realloc, talloc_lock, talloc_unlock);
451 /* Tell parent we're good. */
453 if (write(p->parent_wfd, &err, sizeof(err)) != sizeof(err)) {
459 memmove(&argv[1], &argv[2], --(*argc));
468 /* Say something to our parent (async). */
469 void at_tell_parent(struct at_pool *atp, const void *status)
471 if (atp->p->parent_wfd == -1)
472 errx(1, "This process is not an antithread of this pool");
474 if (write(atp->p->parent_wfd, &status, sizeof(status))!=sizeof(status))
475 err(1, "Failure writing to parent");
478 /* What's the parent saying? Blocks if fd not ready. */
479 void *at_read_parent(struct at_pool *atp)
483 if (atp->p->parent_rfd == -1)
484 errx(1, "This process is not an antithread of this pool");
486 switch (read(atp->p->parent_rfd, &ret, sizeof(ret))) {
488 err(1, "Reading from parent");
495 /* Should never happen. */
496 err(1, "Short read from parent");
500 /* The fd to poll on */
501 int at_parent_fd(struct at_pool *atp)
503 if (atp->p->parent_rfd == -1)
504 errx(1, "This process is not an antithread of this pool");
506 return atp->p->parent_rfd;
509 /* FIXME: Futexme. */
510 void at_lock(void *obj)
512 struct at_pool *atp = talloc_find_parent_bytype(obj, struct at_pool);
516 /* This isn't required yet, but ensures it's a talloc ptr */
517 l = talloc_lock_ptr(obj);
520 lock(atp->p->fd, (char *)obj - (char *)atp->p->pool);
524 errx(1, "Object %p was already locked (something died?)", obj);
529 void at_unlock(void *obj)
531 struct at_pool *atp = talloc_find_parent_bytype(obj, struct at_pool);
535 l = talloc_lock_ptr(obj);
537 errx(1, "Object %p was already unlocked", obj);
540 unlock(atp->p->fd, (char *)obj - (char *)atp->p->pool);
543 void at_lock_all(struct at_pool *atp)
548 void at_unlock_all(struct at_pool *atp)
550 unlock(atp->p->fd, 0);