about summary refs log tree commit diff
diff options
context:
space:
mode:
-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;
+}