diff --git a/lib/isc/trampoline.c b/lib/isc/trampoline.c index fe01394b9e..58171d9138 100644 --- a/lib/isc/trampoline.c +++ b/lib/isc/trampoline.c @@ -15,9 +15,9 @@ #include #include +#include #include -#include #include #include #include @@ -34,14 +34,6 @@ struct isc__trampoline { void *jemalloc_enforce_init; }; -static isc_once_t isc__trampoline_initialize_once = ISC_ONCE_INIT; -static isc_once_t isc__trampoline_shutdown_once = ISC_ONCE_INIT; -static isc_mutex_t isc__trampoline_lock; -static isc__trampoline_t **trampolines; -thread_local size_t isc_tid_v = SIZE_MAX; -static size_t isc__trampoline_min = 1; -static size_t isc__trampoline_max = 65; - /* * We can't use isc_mem API here, because it's called too early and the * isc_mem_debugging flags can be changed later causing mismatch between flags @@ -50,7 +42,19 @@ static size_t isc__trampoline_max = 65; * Since this is a single allocation at library load and deallocation at library * unload, using the standard allocator without the tracking is fine for this * single purpose. + * + * We can't use isc_mutex API either, because we track whether the mutexes get + * properly destroyed, and we intentionally leak the static mutex here without + * destroying it to prevent data race between library destructor running while + * thread is being still created. */ + +static uv_mutex_t isc__trampoline_lock; +static isc__trampoline_t **trampolines; +thread_local size_t isc_tid_v = SIZE_MAX; +static size_t isc__trampoline_min = 1; +static size_t isc__trampoline_max = 65; + static isc__trampoline_t * isc__trampoline_new(int tid, isc_threadfunc_t start, isc_threadarg_t arg) { isc__trampoline_t *trampoline = calloc(1, sizeof(*trampoline)); @@ -66,17 +70,17 @@ isc__trampoline_new(int tid, isc_threadfunc_t start, isc_threadarg_t arg) { return (trampoline); } -static void -trampoline_initialize(void) { - isc_mutex_init(&isc__trampoline_lock); +void +isc__trampoline_initialize(void) { + uv_mutex_init(&isc__trampoline_lock); trampolines = calloc(isc__trampoline_max, sizeof(trampolines[0])); RUNTIME_CHECK(trampolines != NULL); /* Get the trampoline slot 0 for the main thread */ trampolines[0] = isc__trampoline_new(0, NULL, NULL); - trampolines[0]->self = isc_thread_self(); isc_tid_v = trampolines[0]->tid; + trampolines[0]->self = isc_thread_self(); /* Initialize the other trampolines */ for (size_t i = 1; i < isc__trampoline_max; i++) { @@ -86,38 +90,22 @@ trampoline_initialize(void) { } void -isc__trampoline_initialize(void) { - isc_result_t result = isc_once_do(&isc__trampoline_initialize_once, - trampoline_initialize); - RUNTIME_CHECK(result == ISC_R_SUCCESS); -} - -static void -trampoline_shutdown(void) { +isc__trampoline_shutdown(void) { /* * When the program using the library exits abruptly and the library * gets unloaded, there might be some existing trampolines from unjoined * threads. We intentionally ignore those and don't check whether all - * trampolines have been cleared before exiting. + * trampolines have been cleared before exiting, so we leak a little bit + * of resources here, including the lock. */ free(trampolines[0]); - free(trampolines); - trampolines = NULL; - isc_mutex_destroy(&isc__trampoline_lock); -} - -void -isc__trampoline_shutdown(void) { - isc_result_t result = isc_once_do(&isc__trampoline_shutdown_once, - trampoline_shutdown); - RUNTIME_CHECK(result == ISC_R_SUCCESS); } isc__trampoline_t * isc__trampoline_get(isc_threadfunc_t start, isc_threadarg_t arg) { isc__trampoline_t **tmp = NULL; isc__trampoline_t *trampoline = NULL; - LOCK(&isc__trampoline_lock); + uv_mutex_lock(&isc__trampoline_lock); again: for (size_t i = isc__trampoline_min; i < isc__trampoline_max; i++) { if (trampolines[i] == NULL) { @@ -141,17 +129,17 @@ again: goto again; done: INSIST(trampoline != NULL); - UNLOCK(&isc__trampoline_lock); + uv_mutex_unlock(&isc__trampoline_lock); return (trampoline); } void isc__trampoline_detach(isc__trampoline_t *trampoline) { - LOCK(&isc__trampoline_lock); - REQUIRE(trampoline->tid > 0 && - (size_t)trampoline->tid < isc__trampoline_max); + uv_mutex_lock(&isc__trampoline_lock); REQUIRE(trampoline->self == isc_thread_self()); + REQUIRE(trampoline->tid > 0); + REQUIRE((size_t)trampoline->tid < isc__trampoline_max); REQUIRE(trampolines[trampoline->tid] == trampoline); trampolines[trampoline->tid] = NULL; @@ -163,15 +151,17 @@ isc__trampoline_detach(isc__trampoline_t *trampoline) { free(trampoline->jemalloc_enforce_init); free(trampoline); - UNLOCK(&isc__trampoline_lock); + uv_mutex_unlock(&isc__trampoline_lock); return; } void isc__trampoline_attach(isc__trampoline_t *trampoline) { - REQUIRE(trampoline->tid > 0 && - (size_t)trampoline->tid < isc__trampoline_max); + uv_mutex_lock(&isc__trampoline_lock); REQUIRE(trampoline->self == ISC__TRAMPOLINE_UNUSED); + REQUIRE(trampoline->tid > 0); + REQUIRE((size_t)trampoline->tid < isc__trampoline_max); + REQUIRE(trampolines[trampoline->tid] == trampoline); /* Initialize the trampoline */ isc_tid_v = trampoline->tid; @@ -185,6 +175,7 @@ isc__trampoline_attach(isc__trampoline_t *trampoline) { * malloc() + free() calls altogether, as it would foil the fix. */ trampoline->jemalloc_enforce_init = malloc(8); + uv_mutex_unlock(&isc__trampoline_lock); } isc_threadresult_t