mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-04 16:45:24 +00:00
Add isc_trampoline API to have simple accounting around threads
The current isc_hp API uses internal tid_v variable that gets incremented for each new thread using hazard pointers. This tid_v variable is then used as a index to global shared table with hazard pointers state. Since the tid_v is only incremented and never decremented the table could overflow very quickly if we create set of threads for short period of time, they finish the work and cease to exist. Then we create identical set of threads and so on and so on. This is not a problem for a normal `named` operation as the set of threads is stable, but the problematic place are the unit tests where we test network manager or other APIs (task, timer) that create threads. This commits adds a thin wrapper around any function called from isc_thread_create() that adds unique-but-reusable small digit thread id that can be used as index to f.e. hazard pointer tables. The trampoline wrapper ensures that the thread ids will be reused, so the highest thread_id number doesn't grow indefinitely when threads are created and destroyed and then created again. This fixes the hazard pointer table overflow on machines with many cores. [GL #2396]
This commit is contained in:
@@ -213,6 +213,8 @@ libisc_la_SOURCES = \
|
||||
taskpool.c \
|
||||
timer.c \
|
||||
tls.c \
|
||||
trampoline.c \
|
||||
trampoline_p.h \
|
||||
tm.c \
|
||||
utf8.c \
|
||||
pthreads/condition.c \
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "mem_p.h"
|
||||
#include "tls_p.h"
|
||||
#include "trampoline_p.h"
|
||||
|
||||
/***
|
||||
*** Functions
|
||||
@@ -38,10 +39,12 @@ void
|
||||
isc__initialize(void) {
|
||||
isc__mem_initialize();
|
||||
isc__tls_initialize();
|
||||
isc__trampoline_initialize();
|
||||
}
|
||||
|
||||
void
|
||||
isc__shutdown(void) {
|
||||
isc__trampoline_shutdown();
|
||||
isc__tls_shutdown();
|
||||
isc__mem_shutdown();
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@
|
||||
#include <isc/lang.h>
|
||||
#include <isc/result.h>
|
||||
|
||||
extern thread_local size_t isc_tid_v;
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
||||
typedef pthread_t isc_thread_t;
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include <isc/thread.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "trampoline_p.h"
|
||||
|
||||
#ifndef THREAD_MINSTACKSIZE
|
||||
#define THREAD_MINSTACKSIZE (1024U * 1024)
|
||||
#endif /* ifndef THREAD_MINSTACKSIZE */
|
||||
@@ -45,6 +47,10 @@ void
|
||||
isc_thread_create(isc_threadfunc_t func, isc_threadarg_t arg,
|
||||
isc_thread_t *thread) {
|
||||
pthread_attr_t attr;
|
||||
isc__trampoline_t *trampoline_arg;
|
||||
|
||||
trampoline_arg = isc__trampoline_get(func, arg);
|
||||
|
||||
#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
|
||||
defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
|
||||
size_t stacksize;
|
||||
@@ -70,7 +76,8 @@ isc_thread_create(isc_threadfunc_t func, isc_threadarg_t arg,
|
||||
#endif /* if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
|
||||
* defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) */
|
||||
|
||||
ret = pthread_create(thread, &attr, func, arg);
|
||||
ret = pthread_create(thread, &attr, isc__trampoline_run,
|
||||
trampoline_arg);
|
||||
if (ret != 0) {
|
||||
_FATAL(ret, "pthread_create()");
|
||||
}
|
||||
|
189
lib/isc/trampoline.c
Normal file
189
lib/isc/trampoline.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <isc/mem.h>
|
||||
#include <isc/mutex.h>
|
||||
#include <isc/once.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "trampoline_p.h"
|
||||
|
||||
#define ISC__TRAMPOLINE_UNUSED 0
|
||||
|
||||
struct isc__trampoline {
|
||||
int tid; /* const */
|
||||
isc_thread_t self;
|
||||
isc_threadfunc_t start;
|
||||
isc_threadarg_t arg;
|
||||
};
|
||||
|
||||
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 when the isc_mem_debugging flags are changed
|
||||
* later and ISC_MEM_DEBUGSIZE or ISC_MEM_DEBUGCTX flags are
|
||||
* added, neither isc_mem_put() nor isc_mem_free() can be used
|
||||
* to free up the memory allocated here because the flags were
|
||||
* not set when calling isc_mem_get() or isc_mem_allocate()
|
||||
* here.
|
||||
*
|
||||
* Actually, 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 purpose.
|
||||
*/
|
||||
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));
|
||||
RUNTIME_CHECK(trampoline != NULL);
|
||||
|
||||
*trampoline = (isc__trampoline_t){
|
||||
.tid = tid,
|
||||
.start = start,
|
||||
.arg = arg,
|
||||
.self = ISC__TRAMPOLINE_UNUSED,
|
||||
};
|
||||
|
||||
return (trampoline);
|
||||
}
|
||||
|
||||
static void
|
||||
trampoline_initialize(void) {
|
||||
isc_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;
|
||||
|
||||
/* Initialize the other trampolines */
|
||||
for (size_t i = 1; i < isc__trampoline_max; i++) {
|
||||
trampolines[i] = NULL;
|
||||
}
|
||||
isc__trampoline_min = 1;
|
||||
}
|
||||
|
||||
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) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
again:
|
||||
for (size_t i = isc__trampoline_min; i < isc__trampoline_max; i++) {
|
||||
if (trampolines[i] == NULL) {
|
||||
trampoline = isc__trampoline_new(i, start, arg);
|
||||
trampolines[i] = trampoline;
|
||||
isc__trampoline_min = i + 1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
tmp = calloc(2 * isc__trampoline_max, sizeof(trampolines[0]));
|
||||
RUNTIME_CHECK(tmp != NULL);
|
||||
for (size_t i = 0; i < isc__trampoline_max; i++) {
|
||||
tmp[i] = trampolines[i];
|
||||
}
|
||||
for (size_t i = isc__trampoline_max; i < 2 * isc__trampoline_max; i++) {
|
||||
tmp[i] = NULL;
|
||||
}
|
||||
free(trampolines);
|
||||
trampolines = tmp;
|
||||
isc__trampoline_max = isc__trampoline_max * 2;
|
||||
goto again;
|
||||
done:
|
||||
INSIST(trampoline != NULL);
|
||||
UNLOCK(&isc__trampoline_lock);
|
||||
|
||||
return (trampoline);
|
||||
}
|
||||
|
||||
static void
|
||||
trampoline_put(isc__trampoline_t *trampoline) {
|
||||
LOCK(&isc__trampoline_lock);
|
||||
REQUIRE(trampoline->tid > 0 &&
|
||||
(size_t)trampoline->tid < isc__trampoline_max);
|
||||
REQUIRE(trampoline->self == isc_thread_self());
|
||||
REQUIRE(trampolines[trampoline->tid] == trampoline);
|
||||
|
||||
trampolines[trampoline->tid] = NULL;
|
||||
|
||||
if (isc__trampoline_min > (size_t)trampoline->tid) {
|
||||
isc__trampoline_min = trampoline->tid;
|
||||
}
|
||||
|
||||
free(trampoline);
|
||||
|
||||
UNLOCK(&isc__trampoline_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
isc_threadresult_t
|
||||
isc__trampoline_run(isc_threadarg_t arg) {
|
||||
isc__trampoline_t *trampoline = (isc__trampoline_t *)arg;
|
||||
isc_threadresult_t result;
|
||||
|
||||
REQUIRE(trampoline->tid > 0 &&
|
||||
(size_t)trampoline->tid < isc__trampoline_max);
|
||||
REQUIRE(trampoline->self == ISC__TRAMPOLINE_UNUSED);
|
||||
|
||||
/* Initialize the trampoline */
|
||||
isc_tid_v = trampoline->tid;
|
||||
trampoline->self = isc_thread_self();
|
||||
|
||||
/* Run the main function */
|
||||
result = (trampoline->start)(trampoline->arg);
|
||||
|
||||
trampoline_put(trampoline);
|
||||
|
||||
return (result);
|
||||
}
|
53
lib/isc/trampoline_p.h
Normal file
53
lib/isc/trampoline_p.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <isc/thread.h>
|
||||
|
||||
typedef struct isc__trampoline isc__trampoline_t;
|
||||
|
||||
void
|
||||
isc__trampoline_initialize(void);
|
||||
/*%<
|
||||
* Initialize the thread trampoline internal structures, must be called only
|
||||
* once as a library constructor (see lib/isc/lib.c).
|
||||
*/
|
||||
|
||||
void
|
||||
isc__trampoline_shutdown(void);
|
||||
/*%<
|
||||
* Destroy the thread trampoline internal structures, must be called only
|
||||
* once as a library destructor (see lib/isc/lib.c).
|
||||
*/
|
||||
|
||||
isc__trampoline_t *
|
||||
isc__trampoline_get(isc_threadfunc_t start_routine, isc_threadarg_t arg);
|
||||
/*%<
|
||||
* Get a free thread trampoline structure and initialize it with
|
||||
* start_routine and arg passed to start_routine.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'start_routine' is a valid non-NULL thread start_routine
|
||||
*/
|
||||
|
||||
isc_threadresult_t
|
||||
isc__trampoline_run(isc_threadarg_t arg);
|
||||
/*%<
|
||||
* Run the thread trampoline, this will get passed to the actual
|
||||
* pthread_create() (or Windows equivalent), initialize the isc_tid_v.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'arg' is a valid isc_trampoline_t
|
||||
*
|
||||
* Returns:
|
||||
*\li return value from start_routine (see isc__trampoline_get())
|
||||
*/
|
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "mem_p.h"
|
||||
#include "tls_p.h"
|
||||
#include "trampoline_p.h"
|
||||
|
||||
/*
|
||||
* Called when we enter the DLL
|
||||
@@ -32,6 +33,7 @@ __declspec(dllexport) BOOL WINAPI
|
||||
case DLL_PROCESS_ATTACH:
|
||||
isc__mem_initialize();
|
||||
isc__tls_initialize();
|
||||
isc__trampoline_initialize();
|
||||
break;
|
||||
|
||||
/*
|
||||
@@ -39,6 +41,7 @@ __declspec(dllexport) BOOL WINAPI
|
||||
* termination or a call to FreeLibrary.
|
||||
*/
|
||||
case DLL_PROCESS_DETACH:
|
||||
isc__trampoline_shutdown();
|
||||
isc__tls_shutdown();
|
||||
isc__mem_shutdown();
|
||||
break;
|
||||
|
@@ -17,6 +17,8 @@
|
||||
#include <isc/lang.h>
|
||||
#include <isc/result.h>
|
||||
|
||||
extern thread_local size_t isc_tid_v;
|
||||
|
||||
/*
|
||||
* Inlines to help with wait return checking
|
||||
*/
|
||||
|
@@ -715,6 +715,10 @@ isc__tls_shutdown
|
||||
isc_tlsctx_createclient
|
||||
isc_tlsctx_createserver
|
||||
isc_tlsctx_free
|
||||
isc__trampoline_initialize
|
||||
isc__trampoline_shutdown
|
||||
isc__trampoline_get
|
||||
isc__trampoline_run
|
||||
isc_tm_timegm
|
||||
isc_tm_strptime
|
||||
isc_url_parse
|
||||
@@ -796,6 +800,7 @@ isc_commandline_reset DATA
|
||||
isc_dscp_check_value DATA
|
||||
isc_hashctx DATA
|
||||
isc_mem_debugging DATA
|
||||
isc_tid_v DATA
|
||||
@IF PKCS11
|
||||
pk11_verbose_init DATA
|
||||
@END PKCS11
|
||||
|
@@ -358,6 +358,9 @@
|
||||
<ClInclude Include="..\openssl_shim.h">
|
||||
<Filter>Win32 Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\trampoline_p.h">
|
||||
<Filter>Win32 Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="errno2result.h">
|
||||
<Filter>Win32 Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -614,6 +617,9 @@
|
||||
<ClCompile Include="..\tlsstream.c">
|
||||
<Filter>Library Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\trampoline.c">
|
||||
<Filter>Library Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\tm.c">
|
||||
<Filter>Library Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@@ -378,6 +378,7 @@ copy InstallFiles ..\Build\Release\
|
||||
<ClInclude Include="..\entropy_private.h" />
|
||||
<ClInclude Include="..\fsaccess_common_p.h" />
|
||||
<ClInclude Include="..\openssl_shim.h" />
|
||||
<ClInclude Include="..\trampoline_p.h" />
|
||||
<ClInclude Include="syslog.h" />
|
||||
<ClInclude Include="unistd.h" />
|
||||
<ClInclude Include="..\..\versions.h" />
|
||||
@@ -449,6 +450,7 @@ copy InstallFiles ..\Build\Release\
|
||||
<ClCompile Include="..\task.c" />
|
||||
<ClCompile Include="..\taskpool.c" />
|
||||
<ClCompile Include="..\timer.c" />
|
||||
<ClCompile Include="..\trampoline.c" />
|
||||
<ClCompile Include="..\tls.c" />
|
||||
<ClCompile Include="..\tm.c" />
|
||||
<ClCompile Include="..\url.c" />
|
||||
|
@@ -11,17 +11,25 @@
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#include <isc/mutex.h>
|
||||
#include <isc/once.h>
|
||||
#include <isc/strerr.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "trampoline_p.h"
|
||||
|
||||
void
|
||||
isc_thread_create(isc_threadfunc_t start, isc_threadarg_t arg,
|
||||
isc_thread_t *threadp) {
|
||||
isc_thread_t thread;
|
||||
unsigned int id;
|
||||
isc__trampoline_t *trampoline_arg;
|
||||
|
||||
thread = (isc_thread_t)_beginthreadex(NULL, 0, start, arg, 0, &id);
|
||||
trampoline_arg = isc__trampoline_get(start, arg);
|
||||
|
||||
thread = (isc_thread_t)_beginthreadex(NULL, 0, isc__trampoline_run,
|
||||
trampoline_arg, 0, &id);
|
||||
if (thread == NULL) {
|
||||
char strbuf[ISC_STRERRORSIZE];
|
||||
strerror_r(errno, strbuf, sizeof(strbuf));
|
||||
|
@@ -1996,6 +1996,8 @@
|
||||
./lib/isc/tls.c C 2021
|
||||
./lib/isc/tls_p.h C 2021
|
||||
./lib/isc/tm.c C 2014,2016,2018,2019,2020,2021
|
||||
./lib/isc/trampoline.c C 2021
|
||||
./lib/isc/trampoline_p.h C 2021
|
||||
./lib/isc/unix/dir.c C 1999,2000,2001,2004,2005,2007,2008,2009,2011,2012,2016,2017,2018,2019,2020,2021
|
||||
./lib/isc/unix/errno.c C 2016,2018,2019,2020,2021
|
||||
./lib/isc/unix/errno2result.c C 2000,2001,2002,2004,2005,2007,2011,2012,2013,2016,2018,2019,2020,2021
|
||||
|
Reference in New Issue
Block a user