Fix: join worker thread in call_rcu_data_free
[userspace-rcu.git] / src / urcu-call-rcu-impl.h
index 9f85d55b403c5fa1f882947af021c6cae460d712..cb010687829959f1750242dadb6ecfa99b9cd912 100644 (file)
@@ -81,6 +81,10 @@ struct call_rcu_completion_work {
        struct call_rcu_completion *completion;
 };
 
+enum crdf_flags {
+       CRDF_FLAG_JOIN_THREAD = (1 << 0),
+};
+
 /*
  * List of all call_rcu_data structures to keep valgrind happy.
  * Protected by call_rcu_mutex.
@@ -102,7 +106,6 @@ static pthread_mutex_t call_rcu_mutex = PTHREAD_MUTEX_INITIALIZER;
 static struct call_rcu_data *default_call_rcu_data;
 
 static struct urcu_atfork *registered_rculfhash_atfork;
-static unsigned long registered_rculfhash_atfork_refcount;
 
 /*
  * If the sched_getcpu() and sysconf(_SC_NPROCESSORS_CONF) calls are
@@ -766,7 +769,8 @@ void call_rcu(struct rcu_head *head,
  * a list corruption bug in the 0.7.x series. The equivalent fix
  * appeared in 0.6.8 for the stable-0.6 branch.
  */
-void call_rcu_data_free(struct call_rcu_data *crdp)
+static
+void _call_rcu_data_free(struct call_rcu_data *crdp, unsigned int flags)
 {
        if (crdp == NULL || crdp == default_call_rcu_data) {
                return;
@@ -795,9 +799,21 @@ void call_rcu_data_free(struct call_rcu_data *crdp)
        cds_list_del(&crdp->list);
        call_rcu_unlock(&call_rcu_mutex);
 
+       if (flags & CRDF_FLAG_JOIN_THREAD) {
+               int ret;
+
+               ret = pthread_join(get_call_rcu_thread(crdp), NULL);
+               if (ret)
+                       urcu_die(ret);
+       }
        free(crdp);
 }
 
+void call_rcu_data_free(struct call_rcu_data *crdp)
+{
+       _call_rcu_data_free(crdp, CRDF_FLAG_JOIN_THREAD);
+}
+
 /*
  * Clean up all the per-CPU call_rcu threads.
  */
@@ -1022,26 +1038,30 @@ void call_rcu_after_fork_child(void)
                if (crdp == default_call_rcu_data)
                        continue;
                uatomic_set(&crdp->flags, URCU_CALL_RCU_STOPPED);
-               call_rcu_data_free(crdp);
+               /*
+                * Do not join the thread because it does not exist in
+                * the child.
+                */
+               _call_rcu_data_free(crdp, 0);
        }
 }
 
 void urcu_register_rculfhash_atfork(struct urcu_atfork *atfork)
 {
+       if (CMM_LOAD_SHARED(registered_rculfhash_atfork))
+               return;
        call_rcu_lock(&call_rcu_mutex);
-       if (registered_rculfhash_atfork_refcount++)
-               goto end;
-       registered_rculfhash_atfork = atfork;
-end:
+       if (!registered_rculfhash_atfork)
+               registered_rculfhash_atfork = atfork;
        call_rcu_unlock(&call_rcu_mutex);
 }
 
+/*
+ * This unregistration function is deprecated, meant only for internal
+ * use by rculfhash.
+ */
+__attribute__((noreturn))
 void urcu_unregister_rculfhash_atfork(struct urcu_atfork *atfork __attribute__((unused)))
 {
-       call_rcu_lock(&call_rcu_mutex);
-       if (--registered_rculfhash_atfork_refcount)
-               goto end;
-       registered_rculfhash_atfork = NULL;
-end:
-       call_rcu_unlock(&call_rcu_mutex);
+       urcu_die(EPERM);
 }
This page took 0.024806 seconds and 4 git commands to generate.