From: Arran Cudbard-Bell Date: Tue, 4 Jan 2022 21:29:16 +0000 (-0600) Subject: Flesh out the atexit debug functions more X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5df98d5c1ae160be455f1dd5323f1826fd77dfb0;p=thirdparty%2Ffreeradius-server.git Flesh out the atexit debug functions more --- diff --git a/src/lib/eap_aka_sim/crypto.c b/src/lib/eap_aka_sim/crypto.c index 7efa392918..a37f1b3640 100644 --- a/src/lib/eap_aka_sim/crypto.c +++ b/src/lib/eap_aka_sim/crypto.c @@ -85,7 +85,7 @@ EVP_CIPHER_CTX *aka_sim_crypto_cipher_ctx(void) */ void aka_sim_crypto_cipher_ctx_free(void) { - fr_atexit_trigger(_evp_cipher_ctx_free_on_exit); + fr_atexit_trigger(false, _evp_cipher_ctx_free_on_exit, NULL); evp_chipher_ctx = NULL; } diff --git a/src/lib/server/trunk_tests.c b/src/lib/server/trunk_tests.c index 288cda821e..a5c593e33d 100644 --- a/src/lib/server/trunk_tests.c +++ b/src/lib/server/trunk_tests.c @@ -1054,7 +1054,6 @@ static void test_partial_to_complete_states(void) talloc_free(ctx); } -#if 0 /* * Test calling reconnect with requests in each different state */ @@ -1098,7 +1097,7 @@ static void test_requeue_on_reconnect(void) preq->treq = treq; tconn = treq->pub.tconn; /* Store the conn the request was assigned to */ - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); fr_trunk_connection_signal_reconnect(tconn, FR_CONNECTION_FAILED); @@ -1106,36 +1105,39 @@ static void test_requeue_on_reconnect(void) * Should be reassigned to the other connection */ TEST_CHECK(tconn != treq->pub.tconn); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); /* * Should be reassigned to the backlog */ fr_trunk_connection_signal_reconnect(treq->pub.tconn, FR_CONNECTION_FAILED); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_BACKLOG) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_BACKLOG), 1); TEST_CHECK(!treq->pub.tconn); TEST_CASE("cancel on reconnect - FR_TRUNK_REQUEST_STATE_PARTIAL"); + fr_debug_lvl = 4; + /* * Allow the connections to reconnect */ test_time_base = fr_time_add_time_delta(test_time_base, fr_time_delta_from_sec(1)); fr_event_corral(el, test_time_base, false); - fr_event_service(el); + fr_event_service(el); /* run management function */ + fr_event_service(el); /* service any I/O callbacks */ /* * Request should now be assigned back to one of the reconnected * connections. */ - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); TEST_CHECK(treq->pub.tconn != NULL); test_time_base = fr_time_add_time_delta(test_time_base, fr_time_delta_from_sec(1)); fr_event_corral(el, test_time_base, false); /* Send the request (partially) */ fr_event_service(el); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PARTIAL) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PARTIAL), 1); /* * Reconnect the connection. @@ -1153,7 +1155,7 @@ static void test_requeue_on_reconnect(void) preq->cancelled = false; /* Reset */ - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); TEST_CHECK(tconn != treq->pub.tconn); /* Ensure it moved */ TEST_CASE("cancel on reconnect - FR_TRUNK_REQUEST_STATE_SENT"); @@ -1164,12 +1166,12 @@ static void test_requeue_on_reconnect(void) fr_event_corral(el, test_time_base, false); /* Send the request (partially) */ fr_event_service(el); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_SENT) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_SENT), 1); tconn = treq->pub.tconn; fr_trunk_connection_signal_reconnect(treq->pub.tconn, FR_CONNECTION_FAILED); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); /* * Allow the connections to reconnect @@ -1193,7 +1195,7 @@ static void test_requeue_on_reconnect(void) * Signal the request should be cancelled */ fr_trunk_request_signal_cancel(treq); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL), 1); /* * Requests in the cancel state, are @@ -1228,7 +1230,7 @@ static void test_requeue_on_reconnect(void) treq = NULL; fr_trunk_request_enqueue(&treq, trunk, NULL, preq, NULL); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); /* * Sent the request (fully) @@ -1237,10 +1239,10 @@ static void test_requeue_on_reconnect(void) fr_event_corral(el, test_time_base, false); /* Send the request (fully) */ fr_event_service(el); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_SENT) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_SENT), 1); fr_trunk_request_signal_cancel(treq); /* Cancel the request */ - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL), 1); /* * Transition to cancel partial @@ -1249,7 +1251,7 @@ static void test_requeue_on_reconnect(void) fr_event_corral(el, test_time_base, false); fr_event_service(el); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL_PARTIAL) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL_PARTIAL), 1); /* * Trigger a reconnection @@ -1281,7 +1283,7 @@ static void test_requeue_on_reconnect(void) treq = NULL; fr_trunk_request_enqueue(&treq, trunk, NULL, preq, NULL); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_PENDING), 1); /* * Sent the request (fully) @@ -1290,10 +1292,10 @@ static void test_requeue_on_reconnect(void) fr_event_corral(el, test_time_base, false); /* Send the request (fully) */ fr_event_service(el); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_SENT) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_SENT), 1); fr_trunk_request_signal_cancel(treq); /* Cancel the request */ - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL), 1); /* * Transition to cancel @@ -1302,7 +1304,7 @@ static void test_requeue_on_reconnect(void) fr_event_corral(el, test_time_base, false); fr_event_service(el); - TEST_CHECK(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL_SENT) == 1); + TEST_CHECK_LEN(fr_trunk_request_count_by_state(trunk, FR_TRUNK_CONN_ALL, FR_TRUNK_REQUEST_STATE_CANCEL_SENT), 1); /* * Trigger a reconnection @@ -1322,7 +1324,6 @@ static void test_requeue_on_reconnect(void) talloc_free(ctx); } -#endif static void test_connection_start_on_enqueue(void) { @@ -1889,9 +1890,7 @@ TEST_LIST = { { "Enqueue - Basic", test_enqueue_basic }, { "Enqueue - Cancellation points", test_enqueue_cancellation_points }, { "Enqueue - Partial state transitions", test_partial_to_complete_states }, -#if 0 { "Requeue - On reconnect", test_requeue_on_reconnect }, -#endif /* * Rebalance diff --git a/src/lib/util/atexit.c b/src/lib/util/atexit.c index 17642e4f76..45ebb953c7 100644 --- a/src/lib/util/atexit.c +++ b/src/lib/util/atexit.c @@ -33,10 +33,10 @@ RCSID("$Id$") #include -#if defined(DEBUG_THREAD_LOCAL) && !defined(NDEBUG) -# define THREAD_LOCAL_DEBUG FR_FAULT_LOG +#if defined(DEBUG_ATEXIT) && !defined(NDEBUG) +# define ATEXIT_DEBUG FR_FAULT_LOG #else -# define THREAD_LOCAL_DEBUG(...) +# define ATEXIT_DEBUG(...) #endif typedef struct fr_exit_handler_list_s fr_atexit_list_t; @@ -80,7 +80,7 @@ static bool is_exiting; */ static int _atexit_entry_free(fr_atexit_entry_t *e) { - THREAD_LOCAL_DEBUG("%s - Thread %u freeing %p/%p func=%p, uctx=%p (alloced %s:%u)", + ATEXIT_DEBUG("%s - Thread %u freeing %p/%p func=%p, uctx=%p (alloced %s:%u)", __FUNCTION__, (unsigned int)pthread_self(), e->list, e, e->func, e->uctx, e->file, e->line); @@ -115,7 +115,7 @@ static fr_atexit_entry_t *atexit_entry_alloc(NDEBUG_LOCATION_ARGS e->line = line; #endif - THREAD_LOCAL_DEBUG("%s - Thread %u arming %p/%p func=%p, uctx=%p (alloced %s:%u)", + ATEXIT_DEBUG("%s - Thread %u arming %p/%p func=%p, uctx=%p (alloced %s:%u)", __FUNCTION__, (unsigned int)pthread_self(), list, e, e->func, e->uctx, e->file, e->line); @@ -130,7 +130,7 @@ static fr_atexit_entry_t *atexit_entry_alloc(NDEBUG_LOCATION_ARGS */ static int _thread_local_list_free(fr_atexit_list_t *list) { - THREAD_LOCAL_DEBUG("%s - Freeing _Thread_local destructor list %p", + ATEXIT_DEBUG("%s - Freeing _Thread_local destructor list %p", __FUNCTION__, list); fr_dlist_talloc_free(&list->head); /* Free in order */ @@ -152,7 +152,7 @@ static void _thread_local_free(void *list) */ static int _global_list_free(fr_atexit_list_t *list) { - THREAD_LOCAL_DEBUG("%s - Freeing global destructor list %p", + ATEXIT_DEBUG("%s - Freeing global destructor list %p", __FUNCTION__, list); fr_dlist_talloc_free(&list->head); /* Free in order */ @@ -183,7 +183,7 @@ int fr_atexit_global_setup(void) fr_atexit_global = talloc_zero(NULL, fr_atexit_list_t); if (unlikely(!fr_atexit_global)) return -1; - THREAD_LOCAL_DEBUG("%s - Alloced global destructor list %p", __FUNCTION__, fr_atexit_global); + ATEXIT_DEBUG("%s - Alloced global destructor list %p", __FUNCTION__, fr_atexit_global); fr_dlist_talloc_init(&fr_atexit_global->head, fr_atexit_entry_t, entry); talloc_set_destructor(fr_atexit_global, _global_list_free); @@ -191,7 +191,7 @@ int fr_atexit_global_setup(void) fr_atexit_threads = talloc_zero(NULL, fr_atexit_list_t); if (unlikely(!fr_atexit_threads)) return -1; - THREAD_LOCAL_DEBUG("%s - Alloced threads destructor list %p", __FUNCTION__, fr_atexit_threads); + ATEXIT_DEBUG("%s - Alloced threads destructor list %p", __FUNCTION__, fr_atexit_threads); fr_dlist_talloc_init(&fr_atexit_threads->head, fr_atexit_entry_t, entry); talloc_set_destructor(fr_atexit_threads, _global_list_free); @@ -247,7 +247,7 @@ int _fr_atexit_thread_local(NDEBUG_LOCATION_ARGS list = talloc_zero(NULL, fr_atexit_list_t); if (unlikely(!list)) return -1; - THREAD_LOCAL_DEBUG("%s - Thread %u alloced _Thread_local destructor list %p", + ATEXIT_DEBUG("%s - Thread %u alloced _Thread_local destructor list %p", __FUNCTION__, (unsigned int)pthread_self(), list); @@ -290,34 +290,48 @@ int _fr_atexit_thread_local(NDEBUG_LOCATION_ARGS return 0; } -/** Remove destructor +/** Remove a specific destructor for this thread (without executing them) * - * @return - * - 0 on success. - * - -1 if function and uctx could not be found. + * @note This function's primary purpose is to help diagnose issues with destructors + * from within a debugger. + * + * @param[in] uctx_scope Only process entries where the func and scope both match. + * @param[in] func Entries matching this function will be disarmed. + * @param[in] uctx associated with the entry. + * @return How many destructors were disarmed. */ -int fr_atexit_thread_local_disarm(fr_atexit_t func, void const *uctx) +unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx) { - fr_atexit_entry_t *e = NULL; + fr_atexit_entry_t *e = NULL; + unsigned int count = 0; if (!fr_atexit_thread_local) return -1; while ((e = fr_dlist_next(&fr_atexit_thread_local->head, e))) { - if ((e->func == func) && (e->uctx == uctx)) { - THREAD_LOCAL_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)", - __FUNCTION__, - (unsigned int)pthread_self(), - fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line); - fr_dlist_remove(&fr_atexit_thread_local->head, e); - talloc_set_destructor(e, NULL); - talloc_free(e); - return 0; - } + fr_atexit_entry_t *disarm; + + if ((e->func != func) || !uctx_scope || (e->uctx != uctx)) continue; + + ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)", + __FUNCTION__, + (unsigned int)pthread_self(), + fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line); + disarm = e; + e = fr_dlist_remove(&fr_atexit_thread_local->head, e); + talloc_set_destructor(disarm, NULL); + talloc_free(disarm); + + count++; } - return -1; + return count; } +/** Remove all destructors for this thread (without executing them) + * + * @note This function's primary purpose is to help diagnose issues with destructors + * from within a debugger. + */ void fr_atexit_thread_local_disarm_all(void) { fr_atexit_entry_t *e = NULL; @@ -325,10 +339,68 @@ void fr_atexit_thread_local_disarm_all(void) if (!fr_atexit_thread_local) return; while ((e = fr_dlist_pop_head(&fr_atexit_thread_local->head))) { - THREAD_LOCAL_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)", - __FUNCTION__, - (unsigned int)pthread_self(), - fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line); + ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)", + __FUNCTION__, + (unsigned int)pthread_self(), + fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line); + talloc_set_destructor(e, NULL); + talloc_free(e); + } +} + +/** Remove a specific global destructor (without executing it) + * + * @note This function's primary purpose is to help diagnose issues with destructors + * from within a debugger. + * + * @param[in] uctx_scope Only process entries where the func and scope both match. + * @param[in] func Entries matching this function will be disarmed. + * @param[in] uctx associated with the entry. + * @return How many global destructors were disarmed. + */ +unsigned int fr_atexit_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx) +{ + fr_atexit_entry_t *e = NULL; + unsigned int count = 0; + + if (!fr_atexit_thread_local) return -1; + + while ((e = fr_dlist_next(&fr_atexit_global->head, e))) { + fr_atexit_entry_t *disarm; + + if ((e->func != func) || !uctx_scope || (e->uctx != uctx)) continue; + + ATEXIT_DEBUG("%s - Disarming %p/%p func=%p, uctx=%p (alloced %s:%u)", + __FUNCTION__, + fr_atexit_global, e, e->func, e->uctx, e->file, e->line); + + disarm = e; + e = fr_dlist_remove(&fr_atexit_global->head, e); + talloc_set_destructor(disarm, NULL); + talloc_free(disarm); + + count++; + } + + return count; +} + +/** Remove all global destructors (without executing them) + * + * @note This function's primary purpose is to help diagnose issues with destructors + * from within a debugger. + */ +void fr_atexit_disarm_all(void) +{ + fr_atexit_entry_t *e = NULL; + + if (!fr_atexit_global) return; + + while ((e = fr_dlist_pop_head(&fr_atexit_global->head))) { + ATEXIT_DEBUG("%s - Disarming %p/%p func=%p, uctx=%p (alloced %s:%u)", + __FUNCTION__, + fr_atexit_global, e, e->func, e->uctx, e->file, e->line); + talloc_set_destructor(e, NULL); talloc_free(e); } @@ -336,12 +408,17 @@ void fr_atexit_thread_local_disarm_all(void) /** Iterates through all thread local destructor lists, causing destructor to be triggered * - * This should only be called by the main process, and not by threads. + * This should only be called by the main process not by threads. + * + * The main purpose of the function is to force cleanups at a specific time for problematic + * destructors. * - * @param[in] func Entries matching this function will be triggered. + * @param[in] uctx_scope Only process entries where the func and scope both match. + * @param[in] func Entries matching this function will be triggered. + * @param[in] uctx associated with the entry. * @return How many triggers fired. */ -int fr_atexit_trigger(fr_atexit_t func) +unsigned int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx) { fr_atexit_entry_t *e = NULL, *ee; fr_atexit_list_t *list; @@ -350,8 +427,24 @@ int fr_atexit_trigger(fr_atexit_t func) if (!fr_atexit_global) return 0; /* - * Iterate over the list of thread local destructor - * lists. + * Iterate over the global destructors + */ + while ((e = fr_dlist_next(&fr_atexit_global->head, e))) { + if ((e->func != func) || !uctx_scope || (e->uctx != uctx)) continue; + + ATEXIT_DEBUG("%s - Triggering %p/%p func=%p, uctx=%p (alloced %s:%u)", + __FUNCTION__, + fr_atexit_global, e, e->func, e->uctx, e->file, e->line); + + count++; + e = fr_dlist_talloc_free_item(&fr_atexit_global->head, e); + } + e = NULL; + + /* + * Iterate over the list of thread local + * destructor lists running the + * destructors. */ while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) { if (!e->func) continue; /* thread already joined */ @@ -359,7 +452,12 @@ int fr_atexit_trigger(fr_atexit_t func) list = talloc_get_type_abort(e->uctx, fr_atexit_list_t); ee = NULL; while ((ee = fr_dlist_next(&list->head, ee))) { - if (ee->func != func) continue; + if ((ee->func != func) || !uctx_scope || (e->uctx != uctx)) continue; + + ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%u)", + __FUNCTION__, + (unsigned int)pthread_self(), + list, ee, ee->func, ee->uctx, ee->file, ee->line); count++; ee = fr_dlist_talloc_free_item(&list->head, ee); diff --git a/src/lib/util/atexit.h b/src/lib/util/atexit.h index 26af45f4f8..57f14c1629 100644 --- a/src/lib/util/atexit.h +++ b/src/lib/util/atexit.h @@ -26,6 +26,8 @@ */ RCSIDH(atexit_h, "$Id$") +#include + #ifdef __cplusplus extern "C" { #endif @@ -106,11 +108,15 @@ do { \ int _fr_atexit_thread_local(NDEBUG_LOCATION_ARGS fr_atexit_t func, void const *uctx); -int fr_atexit_thread_local_disarm(fr_atexit_t func, void const *uctx); +unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx); + +void fr_atexit_thread_local_disarm_all(void); + +unsigned int fr_atexit_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx); -void fr_atexit_thread_local_disarm_all(void); +void fr_atexit_disarm_all(void); -int fr_atexit_trigger(fr_atexit_t func); +unsigned int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx); #ifdef __cplusplus } diff --git a/src/lib/util/talloc.c b/src/lib/util/talloc.c index 73d637b7fe..6dba9ed25d 100644 --- a/src/lib/util/talloc.c +++ b/src/lib/util/talloc.c @@ -775,7 +775,7 @@ static void _autofree_on_thread_exit(void *af) */ static int _autofree_destructor(TALLOC_CTX *af) { - return fr_atexit_thread_local_disarm(_autofree_on_thread_exit, af); + return fr_atexit_thread_local_disarm(true, _autofree_on_thread_exit, af); } /** Get a thread-safe autofreed ctx that will be freed when the thread or process exits