urcu-bp: Update fork() handling
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 4 Mar 2011 17:25:08 +0000 (12:25 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 4 Mar 2011 17:26:23 +0000 (12:26 -0500)
Introduce

extern void rcu_bp_before_fork(void);
extern void rcu_bp_after_fork_parent(void);
extern void rcu_bp_after_fork_child(void);

to handle fork gracefully. These disable signals and hold the registry mutex
across forks.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
README
urcu-bp.c
urcu-bp.h

diff --git a/README b/README
index b6d2ae4..f7f0dec 100644 (file)
--- a/README
+++ b/README
@@ -201,6 +201,6 @@ Interaction with fork()
        threads) should be released before a fork() is performed, except for the
        rather common scenario where fork() is immediately followed by exec() in
        the child process. The only implementation not subject to that rule is
-       liburcu-bp, which is designed to handle this case by requiring a call to
-       synchronize_rcu() following the fork() in the child before any new
-       thread is created.
+       liburcu-bp, which is designed to handle fork() by calling
+       rcu_bp_before_fork, rcu_bp_after_fork_parent and
+       rcu_bp_after_fork_child.
index db2aef0..04bb675 100644 (file)
--- a/urcu-bp.c
+++ b/urcu-bp.c
@@ -76,6 +76,9 @@ struct registry_arena {
 
 static struct registry_arena registry_arena;
 
+/* Saved fork signal mask, protected by rcu_gp_lock */
+static sigset_t saved_fork_signal_mask;
+
 static void rcu_gc_registry(void);
 
 static void mutex_lock(pthread_mutex_t *mutex)
@@ -331,3 +334,44 @@ void rcu_bp_exit()
 {
        munmap(registry_arena.p, registry_arena.len);
 }
+
+/*
+ * Holding the rcu_gp_lock across fork will make sure we fork() don't race with
+ * a concurrent thread executing with this same lock held. This ensures that the
+ * registry is in a coherent state in the child.
+ */
+void rcu_bp_before_fork(void)
+{
+       sigset_t newmask, oldmask;
+       int ret;
+
+       ret = sigemptyset(&newmask);
+       assert(!ret);
+       ret = pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
+       assert(!ret);
+       mutex_lock(&rcu_gp_lock);
+       saved_fork_signal_mask = oldmask;
+}
+
+void rcu_bp_after_fork_parent(void)
+{
+       sigset_t oldmask;
+       int ret;
+
+       oldmask = saved_fork_signal_mask;
+       mutex_unlock(&rcu_gp_lock);
+       ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
+       assert(!ret);
+}
+
+void rcu_bp_after_fork_child(void)
+{
+       sigset_t oldmask;
+       int ret;
+
+       rcu_gc_registry();
+       oldmask = saved_fork_signal_mask;
+       mutex_unlock(&rcu_gp_lock);
+       ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
+       assert(!ret);
+}
index cca664b..d92fbd1 100644 (file)
--- a/urcu-bp.h
+++ b/urcu-bp.h
@@ -86,6 +86,16 @@ extern void rcu_read_unlock(void);
 
 extern void synchronize_rcu(void);
 
+/*
+ * rcu_bp_before_fork, rcu_bp_after_fork_parent and rcu_bp_after_fork_child
+ * should be called around fork() system calls when the child process is not
+ * expected to immediately perform an exec(). For pthread users, see
+ * pthread_atfork(3).
+ */
+extern void rcu_bp_before_fork(void);
+extern void rcu_bp_after_fork_parent(void);
+extern void rcu_bp_after_fork_child(void);
+
 /*
  * In the bulletproof version, the following functions are no-ops.
  */
This page took 0.032745 seconds and 4 git commands to generate.