+ sigset_t newmask, oldmask;
+ int ret;
+
+ ret = sigfillset(&newmask);
+ if (ret)
+ abort();
+ ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);
+ if (ret)
+ abort();
+
+ mutex_lock(&rcu_registry_lock);
+ remove_thread(rcu_reader_reg);
+ mutex_unlock(&rcu_registry_lock);
+ ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
+ if (ret)
+ abort();
+ rcu_bp_exit();
+}
+
+/*
+ * Remove thread from the registry when it exits, and flag it as
+ * destroyed so garbage collection can take care of it.
+ */
+static
+void urcu_bp_thread_exit_notifier(void *rcu_key)
+{
+ rcu_bp_unregister(rcu_key);
+}
+
+static
+void rcu_sys_membarrier_status(int available)
+{
+ /*
+ * membarrier has blocking behavior, which changes the
+ * application behavior too much compared to using barriers when
+ * synchronize_rcu is used repeatedly (without using call_rcu).
+ * Don't use membarrier for now.
+ */
+}
+
+static
+void rcu_bp_init(void)
+{
+ mutex_lock(&init_lock);
+ if (!rcu_bp_refcount++) {
+ int ret;
+
+ ret = pthread_key_create(&urcu_bp_key,
+ urcu_bp_thread_exit_notifier);
+ if (ret)
+ abort();
+ ret = membarrier(MEMBARRIER_CMD_QUERY, 0);
+ rcu_sys_membarrier_status(ret >= 0
+ && (ret & MEMBARRIER_CMD_SHARED));
+ initialized = 1;
+ }
+ mutex_unlock(&init_lock);
+}
+
+static
+void rcu_bp_exit(void)
+{
+ mutex_lock(&init_lock);
+ if (!--rcu_bp_refcount) {
+ struct registry_chunk *chunk, *tmp;
+ int ret;
+
+ cds_list_for_each_entry_safe(chunk, tmp,
+ ®istry_arena.chunk_list, node) {
+ munmap((void *) chunk, chunk->data_len
+ + sizeof(struct registry_chunk));
+ }
+ CDS_INIT_LIST_HEAD(®istry_arena.chunk_list);
+ ret = pthread_key_delete(urcu_bp_key);
+ if (ret)
+ abort();
+ }
+ mutex_unlock(&init_lock);