11 #include "antithread.h"
12 #include <ccan/noerr/noerr.h>
13 #include <ccan/talloc/talloc.h>
14 #include <ccan/read_write_all/read_write_all.h>
15 #include <ccan/alloc/alloc.h>
17 /* FIXME: Valgrind support should be possible for some cases. Tricky
18 * case is where another process allocates for you, but at worst we
19 * could reset what is valid and what isn't on every entry into the
20 * library or something. */
26 unsigned long poolsize;
28 int parent_rfd, parent_wfd;
37 /* FIXME: Better locking through futexes. */
38 static void lock(int fd, unsigned long off)
43 fl.l_whence = SEEK_SET;
47 while (fcntl(fd, F_SETLKW, &fl) < 0) {
49 err(1, "Failure locking antithread file");
53 static void unlock(int fd, unsigned long off)
59 fl.l_whence = SEEK_SET;
63 fcntl(fd, F_SETLK, &fl);
67 static void *at_realloc(const void *parent, void *ptr, size_t size)
69 struct at_pool *p = talloc_find_parent_bytype(parent, struct at_pool);
70 /* FIXME: realloc in ccan/alloc? */
75 alloc_free(p->pool, p->poolsize, ptr);
77 } else if (ptr == NULL) {
78 /* FIXME: Alignment */
79 new = alloc_get(p->pool, p->poolsize, size, 16);
81 if (size <= alloc_size(p->pool, p->poolsize, ptr))
84 new = alloc_get(p->pool, p->poolsize, size, 16);
87 alloc_size(p->pool, p->poolsize, ptr));
88 alloc_free(p->pool, p->poolsize, ptr);
96 /* We add 16MB to size. This compensates for address randomization. */
97 #define PADDING (16 * 1024 * 1024)
99 /* Create a new sharable pool. */
100 struct at_pool *at_pool(unsigned long size)
106 /* FIXME: How much should we actually add for overhead?. */
107 size += 32 * getpagesize();
109 /* Round up to whole pages. */
110 size = (size + getpagesize()-1) & ~(getpagesize()-1);
122 if (ftruncate(fd, size + PADDING) != 0)
125 p = talloc(NULL, struct at_pool);
129 /* First map gets a nice big area. */
130 p->pool = mmap(NULL, size+PADDING, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
132 if (p->pool == MAP_FAILED)
135 /* Then we remap into the middle of it. */
136 munmap(p->pool, size+PADDING);
137 p->pool = mmap(p->pool + PADDING/2, size, PROT_READ|PROT_WRITE,
139 if (p->pool == MAP_FAILED)
142 /* FIXME: Destructor? */
145 p->parent_rfd = p->parent_wfd = -1;
146 alloc_init(p->pool, p->poolsize);
148 p->ctx = talloc_add_external(p, at_realloc);
155 munmap(p->pool, size);
163 /* Talloc off this to allocate from within the pool. */
164 const void *at_pool_ctx(struct at_pool *atp)
169 static int cant_destroy_self(struct athread *at)
171 /* Perhaps this means we want to detach, but it doesn't really
177 static int destroy_at(struct athread *at)
179 /* If it is already a zombie, this is harmless. */
180 kill(at->pid, SIGTERM);
185 /* FIXME: Should we do SIGKILL if process doesn't exit soon? */
186 if (waitpid(at->pid, NULL, 0) != at->pid)
187 err(1, "Waiting for athread %p (pid %u)", at, at->pid);
192 /* Sets up thread and forks it. NULL on error. */
193 static struct athread *fork_thread(struct at_pool *pool)
198 /* You can't already be a child of this pool. */
199 if (pool->parent_rfd != -1)
200 errx(1, "Can't create antithread on this pool: we're one");
202 /* We don't want this allocated *in* the pool. */
203 at = talloc_steal(pool, talloc(NULL, struct athread));
219 pool->parent_rfd = p2c[0];
220 pool->parent_wfd = c2p[1];
221 talloc_set_destructor(at, cant_destroy_self);
228 talloc_set_destructor(at, destroy_at);
243 /* Creating an antithread via fork() */
244 struct athread *_at_run(struct at_pool *pool,
245 void *(*fn)(struct at_pool *, void *),
250 at = fork_thread(pool);
256 at_tell_parent(pool, fn(pool, obj));
263 static unsigned int num_args(char *const argv[])
267 for (i = 0; argv[i]; i++);
271 /* Fork and execvp, with added arguments for child to grab. */
272 struct athread *at_spawn(struct at_pool *pool, void *arg, char *cmdline[])
277 at = fork_thread(pool);
283 char *argv[num_args(cmdline) + 2];
284 argv[0] = cmdline[0];
285 argv[1] = talloc_asprintf(NULL, "AT:%p/%lu/%i/%i/%i/%p",
286 pool->pool, pool->poolsize,
287 pool->fd, pool->parent_rfd,
288 pool->parent_wfd, arg);
289 /* Copy including NULL terminator. */
290 memcpy(&argv[2], &cmdline[1], num_args(cmdline)*sizeof(char *));
291 execvp(argv[0], argv);
294 write_all(pool->parent_wfd, &err, sizeof(err));
298 /* Child should always write an error code (or 0). */
299 if (read(at->rfd, &err, sizeof(err)) != sizeof(err)) {
312 /* The fd to poll on */
313 int at_fd(struct athread *at)
318 /* What's the antithread saying? Blocks if fd not ready. */
319 void *at_read(struct athread *at)
323 switch (read(at->rfd, &ret, sizeof(ret))) {
325 err(1, "Reading from athread %p (pid %u)", at, at->pid);
332 /* Should never happen. */
333 err(1, "Short read from athread %p (pid %u)", at, at->pid);
337 /* Say something to a child. */
338 void at_tell(struct athread *at, const void *status)
340 if (write(at->wfd, &status, sizeof(status)) != sizeof(status))
341 err(1, "Failure writing to athread %p (pid %u)", at, at->pid);
344 /* For child to grab arguments from command line (removes them) */
345 struct at_pool *at_get_pool(int *argc, char *argv[], void **arg)
347 struct at_pool *p = talloc(NULL, struct at_pool);
356 /* If they don't care, use dummy value. */
360 if (sscanf(argv[1], "AT:%p/%lu/%i/%i/%i/%p",
361 &p->pool, &p->poolsize, &p->fd,
362 &p->parent_rfd, &p->parent_wfd, arg) != 6) {
367 /* FIXME: To try to adjust for address space randomization, we
368 * could re-exec a few times. */
369 map = mmap(p->pool, p->poolsize, PROT_READ|PROT_WRITE, MAP_SHARED,
371 if (map != p->pool) {
372 fprintf(stderr, "Mapping %lu bytes @%p gave %p\n",
373 p->poolsize, p->pool, map);
378 p->ctx = talloc_add_external(p, at_realloc);
382 /* Tell parent we're good. */
384 if (write(p->parent_wfd, &err, sizeof(err)) != sizeof(err)) {
390 memmove(&argv[1], &argv[2], --(*argc));
394 /* FIXME: cleanup properly. */
399 /* Say something to our parent (async). */
400 void at_tell_parent(struct at_pool *pool, const void *status)
402 if (pool->parent_wfd == -1)
403 errx(1, "This process is not an antithread of this pool");
405 if (write(pool->parent_wfd, &status, sizeof(status)) != sizeof(status))
406 err(1, "Failure writing to parent");
409 /* What's the parent saying? Blocks if fd not ready. */
410 void *at_read_parent(struct at_pool *pool)
414 if (pool->parent_rfd == -1)
415 errx(1, "This process is not an antithread of this pool");
417 switch (read(pool->parent_rfd, &ret, sizeof(ret))) {
419 err(1, "Reading from parent");
426 /* Should never happen. */
427 err(1, "Short read from parent");
431 /* The fd to poll on */
432 int at_parent_fd(struct at_pool *pool)
434 if (pool->parent_rfd == -1)
435 errx(1, "This process is not an antithread of this pool");
437 return pool->parent_rfd;
440 /* FIXME: Futexme. */
441 void at_lock(void *obj)
443 struct at_pool *p = talloc_find_parent_bytype(obj, struct at_pool);
447 /* This isn't required yet, but ensures it's a talloc ptr */
448 l = talloc_lock_ptr(obj);
451 lock(p->fd, (char *)obj - (char *)p->pool);
455 errx(1, "Object %p was already locked (something died?)", obj);
460 void at_unlock(void *obj)
462 struct at_pool *p = talloc_find_parent_bytype(obj, struct at_pool);
466 l = talloc_lock_ptr(obj);
468 errx(1, "Object %p was already unlocked", obj);
471 unlock(p->fd, (char *)obj - (char *)p->pool);
474 void at_lock_all(struct at_pool *p)
479 void at_unlock_all(struct at_pool *p)