From: Mathieu Desnoyers Date: Fri, 4 Mar 2011 17:25:08 +0000 (-0500) Subject: urcu-bp: Update fork() handling X-Git-Tag: v0.5.4~1 X-Git-Url: http://git.liburcu.org/?p=urcu.git;a=commitdiff_plain;h=4cf1675fa5feb3a2e9e9f965e516e55fbf411457 urcu-bp: Update fork() handling 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 --- diff --git a/README b/README index b6d2ae4..f7f0dec 100644 --- 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. diff --git a/urcu-bp.c b/urcu-bp.c index db2aef0..04bb675 100644 --- 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); +} diff --git a/urcu-bp.h b/urcu-bp.h index cca664b..d92fbd1 100644 --- 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. */