add pthread_atfork interface

note that this presently does not handle consistency of the libc's own
global state during forking. as per POSIX 2008, if the parent process
was threaded, the child process may only call async-signal-safe
functions until one of the exec-family functions is called, so the
current behavior is believed to be conformant even if non-ideal. it
may be improved at some later time.
This commit is contained in:
Rich Felker 2011-02-18 19:52:42 -05:00
parent 446b4207cc
commit e9417fffb3
4 changed files with 57 additions and 3 deletions

View File

@ -162,6 +162,8 @@ int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, int *);
int pthread_barrierattr_init(pthread_barrierattr_t *);
int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int);
int pthread_atfork(void (*)(void), void (*)(void), void (*)(void));
#include <bits/pthread.h>
int __setjmp(void *);

View File

@ -15,6 +15,7 @@ extern struct libc {
volatile int threads_minus_1;
int (*rsyscall)(int, long, long, long, long, long, long);
void (**tsd_keys)(void *);
void (*fork_handler)(int);
} libc;

View File

@ -1,9 +1,12 @@
#include <unistd.h>
#include "syscall.h"
/* FIXME: add support for atfork stupidity */
#include "libc.h"
pid_t fork(void)
{
return syscall0(__NR_fork);
pid_t ret;
if (libc.fork_handler) libc.fork_handler(-1);
ret = syscall0(__NR_fork);
if (libc.fork_handler) libc.fork_handler(!ret);
return ret;
}

View File

@ -0,0 +1,48 @@
#include <pthread.h>
#include "libc.h"
static struct atfork_funcs {
void (*prepare)(void);
void (*parent)(void);
void (*child)(void);
struct atfork_funcs *prev, *next;
} *funcs;
static int lock;
static void fork_handler(int who)
{
struct atfork_funcs *p;
if (who < 0) {
LOCK(&lock);
for (p=funcs; p; p = p->next) {
if (p->prepare) p->prepare();
funcs = p;
}
} else {
for (p=funcs; p; p = p->prev) {
if (!who && p->parent) p->parent();
else if (who && p->child) p->child();
funcs = p;
}
UNLOCK(&lock);
}
}
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
{
struct atfork_funcs *new = malloc(sizeof *new);
if (!new) return -1;
LOCK(&lock);
libc.fork_handler = fork_handler;
new->next = funcs;
new->prev = 0;
new->prepare = prepare;
new->parent = parent;
new->child = child;
if (funcs) funcs->prev = new;
funcs = new;
UNLOCK(&lock);
return 0;
}