]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Move per-thread RCU setup into isc_thread
authorTony Finch <fanf@isc.org>
Fri, 31 Mar 2023 19:42:47 +0000 (20:42 +0100)
committerOndřej Surý <ondrej@isc.org>
Thu, 27 Apr 2023 10:38:53 +0000 (12:38 +0200)
All the per-loop `libuv` setup remains in `isc_loop`, but the per-thread
RCU setup is moved to `isc_thread` alongside the other per-thread setup.
This avoids repeating the per-thread setup for `call_rcu()` helpers,
and explains a little better why some parts of the per-thread setup
is missing for `call_rcu()` helpers.

This also removes the per-loop `call_rcu()` helpers as we refactored the
isc__random_initialize() in the previous commit.

lib/isc/include/isc/thread.h
lib/isc/include/isc/util.h
lib/isc/loop.c
lib/isc/loop_p.h
lib/isc/thread.c

index f433d063ac13d504bcc1cc89a1ac746606ee9528..a24dfcdca5f59e612cc8bcdc53667013ae9f3145 100644 (file)
@@ -35,6 +35,13 @@ ISC_LANG_BEGINDECLS
 typedef pthread_t isc_thread_t;
 typedef void *(*isc_threadfunc_t)(void *);
 
+/*%
+ * like isc_thread_create(), but run the function on the current
+ * thread which must be the main thread.
+ */
+void
+isc_thread_main(isc_threadfunc_t, void *);
+
 void
 isc_thread_create(isc_threadfunc_t, void *, isc_thread_t *);
 
index b688b20cad48d23727b75c784deaa9eefe0a9354..fd286931d3212b99c24d7419eea98d206d35b6d2 100644 (file)
 #endif /* if __has_feature(thread_sanitizer) */
 
 #if __SANITIZE_THREAD__
+/*
+ * We should rather be including <sanitizer/tsan_interface.h>, but GCC 10
+ * header is broken, so we just make the declarations by hand.
+ */
+void
+__tsan_acquire(void *addr);
+void
+__tsan_release(void *addr);
 #define ISC_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
 #else /* if __SANITIZE_THREAD__ */
 #define ISC_NO_SANITIZE_THREAD
+#define __tsan_acquire(addr)
+#define __tsan_release(addr)
 #endif /* if __SANITIZE_THREAD__ */
 
 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6)
index 5209172a0f60b0ed1d05c63a109134eaf87aac2b..77f3dcc52e0a9418c363650ec127798ddeb1ae98 100644 (file)
@@ -103,6 +103,7 @@ resume_loop(isc_loop_t *loop) {
 
        (void)isc_barrier_wait(&loopmgr->resuming);
        loop->paused = false;
+
        rcu_thread_online();
 }
 
@@ -268,54 +269,17 @@ static void
 quiescent_cb(uv_prepare_t *handle) {
        isc__qsbr_quiescent_cb(handle);
 
-#ifndef RCU_QSBR
-       INSIST(!rcu_read_ongoing());
-#else
+#if defined(RCU_QSBR)
        /* safe memory reclamation */
        rcu_quiescent_state();
 
        /* mark the thread offline when polling */
        rcu_thread_offline();
+#else
+       INSIST(!rcu_read_ongoing());
 #endif
 }
 
-static void
-loop_call_rcu_init(struct rcu_head *rcu_head __attribute__((__unused__))) {
-       /* Work around the jemalloc bug, see trampoline.c for details */
-       void *ptr = malloc(8);
-       free(ptr);
-
-       /* Initialize the random generator in the call_rcu thread */
-       isc__random_initialize();
-}
-
-static void
-loop_run(isc_loop_t *loop) {
-       int r = uv_prepare_start(&loop->quiescent, quiescent_cb);
-       UV_RUNTIME_CHECK(uv_prepare_start, r);
-
-       isc_barrier_wait(&loop->loopmgr->starting);
-
-       isc_async_run(loop, setup_jobs_cb, loop);
-
-       rcu_register_thread();
-
-       struct call_rcu_data *crdp = create_call_rcu_data(0, -1);
-       set_thread_call_rcu_data(crdp);
-
-       call_rcu(&loop->rcu_head, loop_call_rcu_init);
-
-       r = uv_run(&loop->loop, UV_RUN_DEFAULT);
-       UV_RUNTIME_CHECK(uv_run, r);
-
-       rcu_unregister_thread();
-
-       set_thread_call_rcu_data(NULL);
-       call_rcu_data_free(crdp);
-
-       isc_barrier_wait(&loop->loopmgr->stopping);
-}
-
 static void
 loop_close(isc_loop_t *loop) {
        int r = uv_loop_close(&loop->loop);
@@ -337,7 +301,17 @@ loop_thread(void *arg) {
 
        isc__tid_init(loop->tid);
 
-       loop_run(loop);
+       int r = uv_prepare_start(&loop->quiescent, quiescent_cb);
+       UV_RUNTIME_CHECK(uv_prepare_start, r);
+
+       isc_barrier_wait(&loop->loopmgr->starting);
+
+       isc_async_run(loop, setup_jobs_cb, loop);
+
+       r = uv_run(&loop->loop, UV_RUN_DEFAULT);
+       UV_RUNTIME_CHECK(uv_run, r);
+
+       isc_barrier_wait(&loop->loopmgr->stopping);
 
        return (NULL);
 }
@@ -511,7 +485,7 @@ isc_loopmgr_run(isc_loopmgr_t *loopmgr) {
                isc_thread_setname(loop->thread, name);
        }
 
-       loop_thread(&loopmgr->loops[0]);
+       isc_thread_main(loop_thread, &loopmgr->loops[0]);
 }
 
 void
index 373d5df574b3d9b50e635262a3b2e89149e4fbd1..9594a0f306a377e95351fa9b935c9d43327bfcff 100644 (file)
@@ -28,7 +28,6 @@
 #include <isc/stack.h>
 #include <isc/thread.h>
 #include <isc/types.h>
-#include <isc/urcu.h>
 #include <isc/uv.h>
 #include <isc/work.h>
 
@@ -80,8 +79,6 @@ struct isc_loop {
        uv_async_t wakeup_trigger;
        uv_prepare_t quiescent;
        isc_qsbr_phase_t qsbr_phase;
-
-       struct rcu_head rcu_head;
 };
 
 /*
index c0a1607db4626e9d830edf8c451dbb55fad0cd2b..aca7c38332f927fa59aa4ac3925bced4def305af 100644 (file)
 
 #include <stdlib.h>
 
+#include <isc/atomic.h>
 #include <isc/iterated_hash.h>
 #include <isc/strerr.h>
 #include <isc/thread.h>
+#include <isc/tid.h>
+#include <isc/urcu.h>
 #include <isc/util.h>
 
 #ifndef THREAD_MINSTACKSIZE
  */
 
 struct thread_wrap {
+       struct rcu_head rcu_head;
        isc_threadfunc_t func;
        void *arg;
-       void (*free)(void *);
 };
 
 static struct thread_wrap *
 thread_wrap(isc_threadfunc_t func, void *arg) {
        struct thread_wrap *wrap = malloc(sizeof(*wrap));
-
-       RUNTIME_CHECK(wrap != NULL);
        *wrap = (struct thread_wrap){
-               .free = free, /* from stdlib */
                .func = func,
                .arg = arg,
        };
@@ -66,30 +66,56 @@ thread_wrap(isc_threadfunc_t func, void *arg) {
 }
 
 static void *
-thread_run(void *arg) {
-       struct thread_wrap *wrap = arg;
-       isc_threadfunc_t wrap_func = wrap->func;
-       void *wrap_arg = wrap->arg;
-       void *result = NULL;
+thread_body(struct thread_wrap *wrap) {
+       isc_threadfunc_t func = wrap->func;
+       void *arg = wrap->arg;
+       void *ret = NULL;
 
        /*
         * Every thread starts with a malloc() call to prevent memory bloat
-        * caused by a jemalloc quirk. To stop an optimizing compiler from
-        * stripping out free(malloc(1)), we call free via a function pointer.
+        * caused by a jemalloc quirk.  We use CMM_ACCESS_ONCE() To stop an
+        * optimizing compiler from stripping out free(malloc(1)).
         */
-       wrap->free(malloc(1));
-       wrap->free(wrap);
+       void *jemalloc_enforce_init = NULL;
+       CMM_ACCESS_ONCE(jemalloc_enforce_init) = malloc(1);
+       free(jemalloc_enforce_init);
+
+       /* Reassure Thread Sanitizer that it is safe to free the wrapper */
+       __tsan_acquire(wrap);
+       free(wrap);
+
+       rcu_register_thread();
+
+       ret = func(arg);
 
-       /* Get a thread-local digest context. */
+       rcu_unregister_thread();
+
+       return (ret);
+}
+
+static void *
+thread_run(void *wrap) {
+       /*
+        * Get a thread-local digest context only in new threads.
+        * The main thread is handled by isc__initialize().
+        */
        isc__iterated_hash_initialize();
 
-       /* Run the main function */
-       result = wrap_func(wrap_arg);
+       void *ret = thread_body(wrap);
 
-       /* Cleanup */
        isc__iterated_hash_shutdown();
 
-       return (result);
+       return (ret);
+}
+
+void
+isc_thread_main(isc_threadfunc_t func, void *arg) {
+       /*
+        * Either this thread has not yet been started, so it can become the
+        * main thread, or it has already been annointed as the chosen zero
+        */
+       REQUIRE(isc_tid() == ISC_TID_UNKNOWN || isc_tid() == 0);
+       thread_body(thread_wrap(func, arg));
 }
 
 void