The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
atexit.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Macros to abstract Thread Local Storage
18 *
19 * Simplifies calling thread local destructors (called when the thread exits).
20 *
21 * @file lib/util/atexit.c
22 *
23 * @copyright 2020-2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 * @copyright 2020 The FreeRADIUS server project
25 */
26
27RCSID("$Id: 8d2651abf6cf4540dfd2e18016dab74eb5913768 $")
28
29#include <freeradius-devel/util/debug.h>
30#include <freeradius-devel/util/dlist.h>
31#include <freeradius-devel/util/atexit.h>
32
33#include <stdatomic.h>
34
35#ifdef HAVE_PTHREADS
36#endif
37
38#if defined(DEBUG_ATEXIT) && !defined(NDEBUG)
39# define ATEXIT_DEBUG FR_FAULT_LOG
40#else
41# define ATEXIT_DEBUG(...)
42#endif
43
45
46/** Entry in exit handler list
47 *
48 */
49typedef struct {
50 fr_dlist_t entry; //!< Entry in the handler dlist.
51 fr_atexit_list_t *list; //!< List this entry is in.
52
53 fr_atexit_t func; //!< Function to call.
54 void *uctx; //!< uctx to pass.
55
56 char const *file; //!< File where this exit handler was added.
57 int line; //!< Line where this exit handler was added.
59
60/** Head of a list of exit handlers
61 *
62 */
64 fr_dlist_head_t head; //!< Head of the list of destructors
65
66 pthread_key_t key; //!< Key used to trigger thread local destructors.
67 fr_atexit_entry_t *e; //!< Inserted into the global exit handler list
68 ///< to ensure this memory is cleaned up.
69};
70
71#ifdef HAVE_PTHREADS
73static fr_atexit_list_t *fr_atexit_threads = NULL;
74static pthread_mutex_t fr_atexit_global_mutex = PTHREAD_MUTEX_INITIALIZER;
75#endif
76
78static bool is_exiting;
80
81/** Latched once main has decided no more thread-local pools should be handed out
82 *
83 * `fr_atexit_thread_trigger_all()` runs every registered thread destructor on
84 * the calling (main) thread, including ones that free `_Thread_local` pools
85 * owned by threads we don't manage (librdkafka's bg threads, perl, etc.). We
86 * can free the underlying chunks but can't reset another thread's TLS slot,
87 * so those threads keep a dangling pointer in their per-thread cache. Code
88 * that lazily allocates a TLS-cached pool (log.c, sbuff.c, strerror.c, ...)
89 * checks this flag *before* reading its TLS slot and falls back to
90 * `talloc_*(NULL, ...)` (or returns NULL) when set, side-stepping the
91 * dangling pointer entirely.
92 *
93 * Set once, never cleared. Relaxed atomic because this is a one-shot signal
94 * with no other state synchronised through it - readers tolerate observing
95 * the old value briefly.
96 */
97static atomic_bool thread_local_alloc_disabled;
98
99/** Call the exit handler
100 *
101 */
103{
104 ATEXIT_DEBUG("%s - Thread %u freeing %p/%p func=%p, uctx=%p (alloced %s:%d)",
105 __FUNCTION__, (unsigned int)pthread_self(),
106 e->list, e, e->func, e->uctx, e->file, e->line);
107
109
110 /*
111 * If the exit handler wasn't disarmed, call it...
112 */
113 if (e->func) e->func(e->uctx);
114
115 return 0;
116}
117
118/** Allocate a new exit handler entry
119 *
120 */
121static fr_atexit_entry_t *atexit_entry_alloc(char const *file, int line,
122 fr_atexit_list_t *list,
123 fr_atexit_t func, void const *uctx)
124{
126
127 e = talloc_zero(list, fr_atexit_entry_t);
128 if (unlikely(!e)) return NULL;
129
130 e->list = list;
131 e->func = func;
132 e->uctx = UNCONST(void *, uctx);
133 e->file = file;
134 e->line = line;
135
136 ATEXIT_DEBUG("%s - Thread %u arming %p/%p func=%p, uctx=%p (alloced %s:%d)",
137 __FUNCTION__, (unsigned int)pthread_self(),
138 list, e, e->func, e->uctx, e->file, e->line);
139
140 fr_dlist_insert_head(&list->head, e);
141 talloc_set_destructor(e, _atexit_entry_free);
142
143 return e;
144}
145
146/** Talloc destructor for freeing list elements in order
147 *
148 */
150{
151 ATEXIT_DEBUG("%s - Freeing destructor list %p", __FUNCTION__, list);
152
153 fr_dlist_talloc_free(&list->head); /* Free in order */
154 return 0;
155}
156
157/** Free any thread-local exit handler lists that pthread_key failed to fre
158 *
159 */
160static void _global_free(void)
161{
162#ifdef HAVE_PTHREADS
163 pthread_mutex_lock(&fr_atexit_global_mutex);
164#endif
165
166 fr_cond_assert_msg(!is_exiting, "Global free function called multiple times");
167 is_exiting = true;
168
169#ifdef HAVE_PTHREADS
170 pthread_mutex_unlock(&fr_atexit_global_mutex);
171 TALLOC_FREE(fr_atexit_threads); /* Forcefully cleanup any thread-specific memory */
172#endif
173 TALLOC_FREE(fr_atexit_global);
174}
175
176/** Setup the atexit handler, should be called at the start of a program's execution
177 *
178 */
180{
181 if (fr_atexit_global) return 0;
182
183 fr_atexit_global = talloc_zero(NULL, fr_atexit_list_t);
184 if (unlikely(!fr_atexit_global)) return -1;
185
186 ATEXIT_DEBUG("%s - Alloced global destructor list %p", __FUNCTION__, fr_atexit_global);
187
189 talloc_set_destructor(fr_atexit_global, _destructor_list_free);
190
191#ifdef HAVE_PTHREADS
192 fr_atexit_threads = talloc_zero(NULL, fr_atexit_list_t);
193 if (unlikely(!fr_atexit_threads)) {
194 TALLOC_FREE(fr_atexit_global);
195 return -1;
196 }
197
198 ATEXIT_DEBUG("%s - Alloced threads destructor list %p", __FUNCTION__, fr_atexit_threads);
199
200 fr_dlist_talloc_init(&fr_atexit_threads->head, fr_atexit_entry_t, entry);
201 talloc_set_destructor(fr_atexit_threads, _destructor_list_free);
202#endif
203
204 atexit(_global_free); /* Call all remaining destructors at process exit */
205
206 return 0;
207}
208
209#ifdef HAVE_PTHREADS
210#define CHECK_GLOBAL_SETUP() \
211do { \
212 int _ret = 0; \
213 pthread_mutex_lock(&fr_atexit_global_mutex); \
214 fr_cond_assert_msg(!is_exiting, "New atexit handlers should not be allocated whilst exiting"); \
215 if (!fr_atexit_global) _ret = fr_atexit_global_setup(); \
216 pthread_mutex_unlock(&fr_atexit_global_mutex); \
217 if (_ret < 0) return _ret; \
218} while(0)
219#else
220#define CHECK_GLOBAL_SETUP() \
221do { \
222 int _ret = 0; \
223 fr_cond_assert_msg(!is_exiting, "New atexit handlers should not be allocated whilst exiting"); \
224 if (!fr_atexit_global) _ret = fr_atexit_global_setup(); \
225 if (_ret < 0) return _ret; \
226} while(0)
227#endif
228
229/** Add a free function to be called when the process exits
230 *
231 */
232int _atexit_global(char const *file, int line, fr_atexit_t func, void const *uctx)
233{
235
236 if (unlikely(atexit_entry_alloc(file, line, fr_atexit_global, func, uctx) == NULL)) return -1;
237
238 return 0;
239}
240
241/** Remove a specific global destructor (without executing it)
242 *
243 * @note This function's primary purpose is to help diagnose issues with destructors
244 * from within a debugger.
245 *
246 * @param[in] uctx_scope Only process entries where the func and scope both match.
247 * @param[in] func Entries matching this function will be disarmed.
248 * @param[in] uctx associated with the entry.
249 * @return How many global destructors were disarmed.
250 */
251unsigned int fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
252{
253 fr_atexit_entry_t *e = NULL;
254 unsigned int count = 0;
255
256 if (!fr_atexit_global) return 0;
257
258 while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
259 fr_atexit_entry_t *disarm;
260
261 if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
262
263 ATEXIT_DEBUG("%s - Disarming %p/%p func=%p, uctx=%p (alloced %s:%d)",
264 __FUNCTION__,
265 fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
266
267 disarm = e;
269 talloc_set_destructor(disarm, NULL);
270 talloc_free(disarm);
271
272 count++;
273 }
274
275 return count;
276}
277
278/** Remove all global destructors (without executing them)
279 *
280 * @note This function's primary purpose is to help diagnose issues with destructors
281 * from within a debugger.
282 */
284{
285 fr_atexit_entry_t *e = NULL;
286
287 if (!fr_atexit_global) return;
288
289 while ((e = fr_dlist_pop_head(&fr_atexit_global->head))) {
290 ATEXIT_DEBUG("%s - Disarming %p/%p func=%p, uctx=%p (alloced %s:%d)",
291 __FUNCTION__,
292 fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
293
294 talloc_set_destructor(e, NULL);
295 talloc_free(e);
296 }
297}
298
299/** Cause all global free triggers to fire
300 *
301 * This is necessary when libraries (perl) register their own
302 * atexit handlers using the normal POSIX mechanism, and we need
303 * to ensure all our atexit handlers fire before so any global
304 * deinit is done explicitly by us.
305 *
306 * @return
307 * - >= 0 The number of atexit handlers triggered on success.
308 * - <0 the return code from any atexit handlers that returned an error.
309 */
311{
312 fr_atexit_entry_t *e = NULL, *to_free;
313 unsigned int count = 0;
314
315 /*
316 * Iterate over the list of thread local
317 * destructor lists running the
318 * destructors.
319 */
320 while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
321 ATEXIT_DEBUG("%s - Triggering %p/%p func=%p, uctx=%p (alloced %s:%d)",
322 __FUNCTION__,
323 fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
324
325 count++;
326 to_free = e;
328 if (talloc_free(to_free) < 0) {
329 fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
330 " (alloced %s:%d)",
331 fr_atexit_global, to_free,
332 to_free->func, to_free->uctx,
333 to_free->file, to_free->line);
334 return -1;
335 }
336 }
337
338 return count;
339}
340
341/** Iterates through all thread local destructor lists, causing destructor to be triggered
342 *
343 * This should only be called by the main process not by threads.
344 *
345 * The main purpose of the function is to force cleanups at a specific time for problematic
346 * destructors.
347 *
348 * @param[in] uctx_scope Only process entries where the func and scope both match.
349 * @param[in] func Entries matching this function will be triggered.
350 * @param[in] uctx associated with the entry.
351 * @return
352 * - >= 0 The number of atexit handlers triggered on success.
353 * - <0 the return code from any atexit handlers that returned an error.
354 */
355int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
356{
357 fr_atexit_entry_t *e = NULL, *to_free;
358#ifdef HAVE_PTHREADS
360 fr_atexit_list_t *list;
361#endif
362 unsigned int count = 0;
363
364 if (!fr_atexit_global) goto do_threads;
365
366 /*
367 * Iterate over the global destructors
368 */
369 while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
370 if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
371
372 ATEXIT_DEBUG("%s - Triggering %p/%p func=%p, uctx=%p (alloced %s:%d)",
373 __FUNCTION__,
374 fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
375
376 count++;
377 to_free = e;
379 if (talloc_free(to_free) < 0) {
380 fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
381 " (alloced %s:%d)",
382 fr_atexit_global, to_free,
383 to_free->func, to_free->uctx,
384 to_free->file, to_free->line);
385 return -1;
386 }
387 }
388 e = NULL;
389
390do_threads:
391#ifdef HAVE_PTHREADS
392 if (!fr_atexit_threads) return 0;
393
394 /*
395 * Iterate over the list of thread local
396 * destructor lists running the
397 * destructors.
398 */
399 while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
400 if (!e->func) continue; /* thread already joined */
401
402 list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
403 ee = NULL;
404 while ((ee = fr_dlist_next(&list->head, ee))) {
405 if ((ee->func != func) || ((ee->uctx != uctx) && uctx_scope)) continue;
406
407 ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%d)",
408 __FUNCTION__,
409 (unsigned int)pthread_self(),
410 list, ee, ee->func, ee->uctx, ee->file, ee->line);
411
412 count++;
413 to_free = ee;
414 ee = fr_dlist_remove(&list->head, ee);
415 if (talloc_free(to_free) < 0) {
416 fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
417 " (alloced %s:%d)",
418 list, to_free,
419 to_free->func, to_free->uctx,
420 to_free->file, to_free->line);
421 return -1;
422 }
423 }
424 }
425#endif
426
427 return count;
428}
429
430
431/** Disable lazy allocation of thread-local caches for the rest of the process
432 *
433 * Call this from main *before* `fr_atexit_thread_trigger_all()`. After it
434 * returns, every TLS-pool initialiser that consults
435 * `fr_atexit_thread_local_alloc_disabled()` falls back to the un-cached path,
436 * so `_Thread_local` slots in other threads aren't read after the trigger
437 * frees their backing memory.
438 */
443
444/** Has @ref fr_atexit_thread_local_disable_alloc been called yet
445 *
446 */
451
452/** Return whether we're currently in the teardown phase
453 *
454 * When this function returns true no more thread local or global
455 * destructors can be added.
456 */
458{
459#ifdef HAVE_PTHREADS
460 bool save_is_exiting;
461
462 pthread_mutex_lock(&fr_atexit_global_mutex);
463 save_is_exiting = is_exiting;
464 pthread_mutex_unlock(&fr_atexit_global_mutex);
465
466 return save_is_exiting;
467#else
468 return is_exiting;
469#endif
470}
471
472#ifdef HAVE_PTHREADS
473/** Talloc destructor for freeing list elements in order
474 *
475 */
476static int _thread_local_list_free(fr_atexit_list_t *list)
477{
478 ATEXIT_DEBUG("%s - Freeing _Thread_local destructor list %p", __FUNCTION__, list);
479
480 fr_dlist_talloc_free(&list->head); /* Free in order */
481 pthread_mutex_lock(&fr_atexit_global_mutex);
482 list->e->func = NULL; /* Disarm the global entry that'd free the thread-specific list */
483 pthread_mutex_unlock(&fr_atexit_global_mutex);
484 return 0;
485}
486
487/** Run all the thread local destructors
488 *
489 * @param[in] list The thread-specific exit handler list.
490 */
491static void _thread_local_pthread_free(void *list)
492{
493 talloc_free(list);
494}
495
496/** Run all the thread local destructors
497 *
498 * @param[in] list The thread-specific exit handler list.
499 */
500static int _thread_local_free(void *list)
501{
502 thread_is_exiting = true;
503 return talloc_free(list);
504}
505
506/** Add a new destructor
507 *
508 * @return
509 * - 0 on success.
510 * - -1 on memory allocation failure;
511 */
512int _fr_atexit_thread_local(char const *file, int line,
513 fr_atexit_t func, void const *uctx)
514{
516
517 /*
518 * Initialise the thread local list, just for pthread_exit().
519 */
521 fr_atexit_list_t *list;
522
523 /*
524 * Must be heap allocated, because thread local
525 * structures can be freed before the key
526 * destructor is run (depending on platform).
527 */
528 list = talloc_zero(NULL, fr_atexit_list_t);
529 if (unlikely(!list)) return -1;
530
531 ATEXIT_DEBUG("%s - Thread %u alloced _Thread_local destructor list %p",
532 __FUNCTION__,
533 (unsigned int)pthread_self(), list);
534
536 (void) pthread_key_create(&list->key, _thread_local_pthread_free);
537
538 /*
539 * We need to pass in a pointer to the heap
540 * memory because, again, the thread local
541 * indirection table may have disappeared
542 * by the time the thread destructor is
543 * called.
544 */
545 (void) pthread_setspecific(list->key, list);
546 talloc_set_destructor(list, _thread_local_list_free);
547
548 /*
549 * Add a destructor for the thread-local list
550 * The pthread based destructor will disarm
551 * this if it fires, but leave it enabled if
552 * it doesn't, thus ensuring the memory is
553 * *always* freed one way or another.
554 */
555 pthread_mutex_lock(&fr_atexit_global_mutex);
556 list->e = atexit_entry_alloc(file, line,
557 fr_atexit_threads,
558 _thread_local_free,
559 list);
560
561 pthread_mutex_unlock(&fr_atexit_global_mutex);
562
564 }
565
566 /*
567 * Now allocate the actual atexit handler entry
568 */
569 if (atexit_entry_alloc(file, line, fr_atexit_thread_local, func, uctx) == NULL) return -1;
570
571 return 0;
572}
573
574/** Remove a specific destructor for this thread (without executing them)
575 *
576 * @note This function's primary purpose is to help diagnose issues with destructors
577 * from within a debugger.
578 *
579 * @param[in] uctx_scope Only process entries where the func and scope both match.
580 * @param[in] func Entries matching this function will be disarmed.
581 * @param[in] uctx associated with the entry.
582 * @return How many destructors were disarmed.
583 */
584unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
585{
586 fr_atexit_entry_t *e = NULL;
587 unsigned int count = 0;
588
589 if (!fr_atexit_thread_local) return 0;
590
591 while ((e = fr_dlist_next(&fr_atexit_thread_local->head, e))) {
592 fr_atexit_entry_t *disarm;
593
594 if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
595
596 ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%d)",
597 __FUNCTION__,
598 (unsigned int)pthread_self(),
599 fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
600 disarm = e;
602 talloc_set_destructor(disarm, NULL);
603 talloc_free(disarm);
604
605 count++;
606 }
607
608 return count;
609}
610
611/** Remove all destructors for this thread (without executing them)
612 *
613 * @note This function's primary purpose is to help diagnose issues with destructors
614 * from within a debugger.
615 */
617{
618 fr_atexit_entry_t *e = NULL;
619
620 if (!fr_atexit_thread_local) return;
621
622 while ((e = fr_dlist_pop_head(&fr_atexit_thread_local->head))) {
623 ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%d)",
624 __FUNCTION__,
625 (unsigned int)pthread_self(),
626 fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
627 talloc_set_destructor(e, NULL);
628 talloc_free(e);
629 }
630}
631
632/** Cause all thread local free triggers to fire
633 *
634 * This is necessary when we're running in single threaded mode
635 * to ensure all "thread-local" memory (which isn't actually thread local)
636 * is cleaned up.
637 *
638 * One example is the OpenSSL log BIOs which must be cleaned up
639 * before fr_openssl_free is called.
640 *
641 * @return
642 * - >= 0 The number of atexit handlers triggered on success.
643 * - <0 the return code from any atexit handlers that returned an error.
644 */
646{
647 fr_atexit_entry_t *e = NULL, *ee, *to_free;
648 fr_atexit_list_t *list;
649 unsigned int count = 0;
650
651 /*
652 * Disable TLS-cached pool handouts before we start freeing
653 * any of them. Threads we don't manage (librdkafka's bg
654 * threads, etc.) keep dangling pointers in their
655 * `_Thread_local` slots once the chunks here are reaped, so
656 * any TLS-pool initialiser that consults the flag falls back
657 * to `talloc_*(NULL, ...)` from now on.
658 */
660
661 /*
662 * Iterate over the list of thread local
663 * destructor lists running the
664 * destructors.
665 */
666 while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
667 if (!e->func) continue; /* thread already joined */
668
669 list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
670 ee = NULL;
671 while ((ee = fr_dlist_next(&list->head, ee))) {
672 ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%d)",
673 __FUNCTION__,
674 (unsigned int)pthread_self(),
675 list, ee, ee->func, ee->uctx, ee->file, ee->line);
676
677 count++;
678 to_free = ee;
679 ee = fr_dlist_remove(&list->head, ee);
680 if (talloc_free(to_free) < 0) {
681 fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
682 " (alloced %s:%d)",
683 list, to_free,
684 to_free->func, to_free->uctx,
685 to_free->file, to_free->line
686 );
687 return -1;
688 }
689 }
690 }
691
692 return count;
693}
694
695/** Return whether the thread is currently being cleaned up
696 *
697 */
698bool fr_atexit_thread_is_exiting(void)
699{
700 return thread_is_exiting;
701}
702#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
bool fr_atexit_thread_local_alloc_disabled(void)
Has fr_atexit_thread_local_disable_alloc been called yet.
Definition atexit.c:447
void fr_atexit_thread_local_disable_alloc(void)
Disable lazy allocation of thread-local caches for the rest of the process.
Definition atexit.c:439
unsigned int fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
Remove a specific global destructor (without executing it)
Definition atexit.c:251
static fr_atexit_list_t * fr_atexit_global
Definition atexit.c:77
char const * file
File where this exit handler was added.
Definition atexit.c:56
#define CHECK_GLOBAL_SETUP()
Definition atexit.c:220
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:179
fr_dlist_t entry
Entry in the handler dlist.
Definition atexit.c:50
static _Thread_local bool thread_is_exiting
Definition atexit.c:79
pthread_key_t key
Key used to trigger thread local destructors.
Definition atexit.c:66
fr_atexit_t func
Function to call.
Definition atexit.c:53
#define ATEXIT_DEBUG(...)
Definition atexit.c:41
fr_dlist_head_t head
Head of the list of destructors.
Definition atexit.c:64
static fr_atexit_entry_t * atexit_entry_alloc(char const *file, int line, fr_atexit_list_t *list, fr_atexit_t func, void const *uctx)
Allocate a new exit handler entry.
Definition atexit.c:121
void fr_atexit_global_disarm_all(void)
Remove all global destructors (without executing them)
Definition atexit.c:283
fr_atexit_entry_t * e
Inserted into the global exit handler list to ensure this memory is cleaned up.
Definition atexit.c:67
static int _atexit_entry_free(fr_atexit_entry_t *e)
Call the exit handler.
Definition atexit.c:102
static atomic_bool thread_local_alloc_disabled
Latched once main has decided no more thread-local pools should be handed out.
Definition atexit.c:97
static bool is_exiting
Definition atexit.c:78
void * uctx
uctx to pass.
Definition atexit.c:54
int line
Line where this exit handler was added.
Definition atexit.c:57
static void _global_free(void)
Free any thread-local exit handler lists that pthread_key failed to fre.
Definition atexit.c:160
bool fr_atexit_is_exiting(void)
Return whether we're currently in the teardown phase.
Definition atexit.c:457
static int _destructor_list_free(fr_atexit_list_t *list)
Talloc destructor for freeing list elements in order.
Definition atexit.c:149
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:310
int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
Iterates through all thread local destructor lists, causing destructor to be triggered.
Definition atexit.c:355
int _atexit_global(char const *file, int line, fr_atexit_t func, void const *uctx)
Add a free function to be called when the process exits.
Definition atexit.c:232
fr_atexit_list_t * list
List this entry is in.
Definition atexit.c:51
Entry in exit handler list.
Definition atexit.c:49
Head of a list of exit handlers.
Definition atexit.c:63
int(* fr_atexit_t)(void *uctx)
Destructor callback.
Definition atexit.h:44
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:236
#define fr_atexit_thread_local_disarm(...)
Definition atexit.h:234
#define _Thread_local
Definition atexit.h:213
#define fr_atexit_thread_local_disarm_all(...)
Definition atexit.h:235
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:224
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define unlikely(_x)
Definition build.h:455
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:148
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition dlist.h:145
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:892
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition dlist.h:320
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
return count
Definition module.c:155
@ memory_order_relaxed
Definition stdatomic.h:127
#define atomic_load_explicit(object, order)
Definition stdatomic.h:312
#define atomic_store_explicit(object, desired, order)
Definition stdatomic.h:314
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84