X-Git-Url: http://git.liburcu.org/?p=userspace-rcu.git;a=blobdiff_plain;f=urcu-bp.c;h=3e5be5fb59e1baf0ed7a7a7b7c26f61603654a98;hp=1386dfc8fdfb82a533aface955c79c6f0b318daf;hb=refs%2Fheads%2Fstable-0.7;hpb=6b702fa4ea05b4f6d37000d5cd26d19f0d0807e0 diff --git a/urcu-bp.c b/urcu-bp.c index 1386dfc..3e5be5f 100644 --- a/urcu-bp.c +++ b/urcu-bp.c @@ -97,9 +97,23 @@ int rcu_bp_refcount; static void __attribute__((constructor)) rcu_bp_init(void); static -void __attribute__((destructor)) rcu_bp_exit(void); +void __attribute__((destructor)) _rcu_bp_exit(void); +/* + * rcu_gp_lock ensures mutual exclusion between threads calling + * synchronize_rcu(). + */ static pthread_mutex_t rcu_gp_lock = PTHREAD_MUTEX_INITIALIZER; +/* + * rcu_registry_lock ensures mutual exclusion between threads + * registering and unregistering themselves to/from the registry, and + * with threads reading that registry from synchronize_rcu(). However, + * this lock is not held all the way through the completion of awaiting + * for the grace period. It is sporadically released between iterations + * on the registry. + * rcu_registry_lock may nest inside rcu_gp_lock. + */ +static pthread_mutex_t rcu_registry_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; static int initialized; @@ -171,6 +185,10 @@ static void mutex_unlock(pthread_mutex_t *mutex) urcu_die(ret); } +/* + * Always called with rcu_registry lock held. Releases this lock between + * iterations and grabs it again. Holds the lock when it returns. + */ void update_counter_and_wait(void) { CDS_LIST_HEAD(qsreaders); @@ -209,10 +227,14 @@ void update_counter_and_wait(void) if (cds_list_empty(®istry)) { break; } else { + /* Temporarily unlock the registry lock. */ + mutex_unlock(&rcu_registry_lock); if (wait_loops >= RCU_QS_ACTIVE_ATTEMPTS) (void) poll(NULL, 0, RCU_SLEEP_DELAY_MS); else caa_cpu_relax(); + /* Re-lock the registry lock before the next loop. */ + mutex_lock(&rcu_registry_lock); } } /* put back the reader list in the registry */ @@ -230,6 +252,7 @@ void synchronize_rcu(void) assert(!ret); mutex_lock(&rcu_gp_lock); + mutex_lock(&rcu_registry_lock); if (cds_list_empty(®istry)) goto out; @@ -241,6 +264,8 @@ void synchronize_rcu(void) /* * Wait for previous parity to be empty of readers. + * update_counter_and_wait() can release and grab again + * rcu_registry_lock interally. */ update_counter_and_wait(); /* 0 -> 1, wait readers in parity 0 */ @@ -253,6 +278,8 @@ void synchronize_rcu(void) /* * Wait for previous parity to be empty of readers. + * update_counter_and_wait() can release and grab again + * rcu_registry_lock interally. */ update_counter_and_wait(); /* 1 -> 0, wait readers in parity 1 */ @@ -262,6 +289,7 @@ void synchronize_rcu(void) */ cmm_smp_mb(); out: + mutex_unlock(&rcu_registry_lock); mutex_unlock(&rcu_gp_lock); ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL); assert(!ret); @@ -465,9 +493,9 @@ void rcu_bp_register(void) */ rcu_bp_init(); - mutex_lock(&rcu_gp_lock); + mutex_lock(&rcu_registry_lock); add_thread(); - mutex_unlock(&rcu_gp_lock); + mutex_unlock(&rcu_registry_lock); end: ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL); if (ret) @@ -488,13 +516,13 @@ void rcu_bp_unregister(struct rcu_reader *rcu_reader_reg) if (ret) abort(); - mutex_lock(&rcu_gp_lock); + mutex_lock(&rcu_registry_lock); remove_thread(rcu_reader_reg); - mutex_unlock(&rcu_gp_lock); + mutex_unlock(&rcu_registry_lock); ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL); if (ret) abort(); - rcu_bp_exit(); + _rcu_bp_exit(); } /* @@ -524,7 +552,7 @@ void rcu_bp_init(void) } static -void rcu_bp_exit(void) +void _rcu_bp_exit(void) { mutex_lock(&init_lock); if (!--rcu_bp_refcount) { @@ -544,9 +572,19 @@ void rcu_bp_exit(void) } /* - * 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. + * Keep ABI compability within stable versions. This has never been + * exposed through a header, but needs to stay in the .so until the + * soname is bumped. + */ +void rcu_bp_exit(void) +{ +} + +/* + * Holding the rcu_gp_lock and rcu_registry_lock across fork will make + * sure we fork() don't race with a concurrent thread executing with + * any of those locks held. This ensures that the registry and data + * protected by rcu_gp_lock are in a coherent state in the child. */ void rcu_bp_before_fork(void) { @@ -558,6 +596,7 @@ void rcu_bp_before_fork(void) ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask); assert(!ret); mutex_lock(&rcu_gp_lock); + mutex_lock(&rcu_registry_lock); saved_fork_signal_mask = oldmask; } @@ -567,6 +606,7 @@ void rcu_bp_after_fork_parent(void) int ret; oldmask = saved_fork_signal_mask; + mutex_unlock(&rcu_registry_lock); mutex_unlock(&rcu_gp_lock); ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL); assert(!ret); @@ -574,7 +614,7 @@ void rcu_bp_after_fork_parent(void) /* * Prune all entries from registry except our own thread. Fits the Linux - * fork behavior. Called with rcu_gp_lock held. + * fork behavior. Called with rcu_gp_lock and rcu_registry_lock held. */ static void urcu_bp_prune_registry(void) @@ -602,6 +642,7 @@ void rcu_bp_after_fork_child(void) urcu_bp_prune_registry(); oldmask = saved_fork_signal_mask; + mutex_unlock(&rcu_registry_lock); mutex_unlock(&rcu_gp_lock); ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL); assert(!ret);