Merge branch 'ondrej-fix-trampoline-locking-v9_18' into 'v9_18'

Lock the trampoline when attaching [v9.18]

See merge request isc-projects/bind9!6298
This commit is contained in:
Ondřej Surý
2022-05-13 12:05:47 +00:00

View File

@@ -15,9 +15,9 @@
#include <inttypes.h>
#include <stdlib.h>
#include <uv.h>
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/once.h>
#include <isc/thread.h>
#include <isc/util.h>
@@ -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