summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-02-18 19:52:42 -0500
committerRich Felker <dalias@aerifal.cx>2011-02-18 19:52:42 -0500
commite9417fffb39c299e556c5ad0c1545f0c02618e3c (patch)
tree90c04bef0567ff4df7c7b57986a756b8b11b506c
parent446b4207cc7a30d8a4d5b2445a5a1b27d440f55d (diff)
downloadmusl-e9417fffb39c299e556c5ad0c1545f0c02618e3c.tar.gz
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.
-rw-r--r--include/pthread.h2
-rw-r--r--src/internal/libc.h1
-rw-r--r--src/process/fork.c9
-rw-r--r--src/thread/pthread_atfork.c48
4 files changed, 57 insertions, 3 deletions
diff --git a/include/pthread.h b/include/pthread.h
index 749a0e8c..19199468 100644
--- a/include/pthread.h
+++ b/include/pthread.h
@@ -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 *);
diff --git a/src/internal/libc.h b/src/internal/libc.h
index e353f363..ea221b6f 100644
--- a/src/internal/libc.h
+++ b/src/internal/libc.h
@@ -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;
diff --git a/src/process/fork.c b/src/process/fork.c
index 1213f0f5..0638ed67 100644
--- a/src/process/fork.c
+++ b/src/process/fork.c
@@ -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;
}
diff --git a/src/thread/pthread_atfork.c b/src/thread/pthread_atfork.c
new file mode 100644
index 00000000..0773dc8f
--- /dev/null
+++ b/src/thread/pthread_atfork.c
@@ -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;
+}