The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
27 RCSID("$Id: de66df56f3c26b149e0987cd952f0a0f655ad4c8 $")
28 
29 #include <freeradius-devel/util/debug.h>
30 #include <freeradius-devel/util/dlist.h>
31 #include <freeradius-devel/util/atexit.h>
32 
33 #ifdef HAVE_PTHREADS
34 #include <pthread.h>
35 #endif
36 
37 #if defined(DEBUG_ATEXIT) && !defined(NDEBUG)
38 # define ATEXIT_DEBUG FR_FAULT_LOG
39 #else
40 # define ATEXIT_DEBUG(...)
41 #endif
42 
44 
45 /** Entry in exit handler list
46  *
47  */
48 typedef struct {
49  fr_dlist_t entry; //!< Entry in the handler dlist.
50  fr_atexit_list_t *list; //!< List this entry is in.
51 
52  fr_atexit_t func; //!< Function to call.
53  void *uctx; //!< uctx to pass.
54 
55  char const *file; //!< File where this exit handler was added.
56  int line; //!< Line where this exit handler was added.
58 
59 /** Head of a list of exit handlers
60  *
61  */
63  fr_dlist_head_t head; //!< Head of the list of destructors
64 
65  pthread_key_t key; //!< Key used to trigger thread local destructors.
66  fr_atexit_entry_t *e; //!< Inserted into the global exit handler list
67  ///< to ensure this memory is cleaned up.
68 };
69 
70 #ifdef HAVE_PTHREADS
71 static _Thread_local fr_atexit_list_t *fr_atexit_thread_local = NULL;
72 static fr_atexit_list_t *fr_atexit_threads = NULL;
73 static pthread_mutex_t fr_atexit_global_mutex = PTHREAD_MUTEX_INITIALIZER;
74 #endif
75 
77 static bool is_exiting;
78 static _Thread_local bool thread_is_exiting;
79 
80 /** Call the exit handler
81  *
82  */
84 {
85  ATEXIT_DEBUG("%s - Thread %u freeing %p/%p func=%p, uctx=%p (alloced %s:%u)",
86  __FUNCTION__, (unsigned int)pthread_self(),
87  e->list, e, e->func, e->uctx, e->file, e->line);
88 
90 
91  /*
92  * If the exit handler wasn't disarmed, call it...
93  */
94  if (e->func) e->func(e->uctx);
95 
96  return 0;
97 }
98 
99 /** Allocate a new exit handler entry
100  *
101  */
103  fr_atexit_list_t *list,
104  fr_atexit_t func, void const *uctx)
105 {
107 
108  e = talloc_zero(list, fr_atexit_entry_t);
109  if (unlikely(!e)) return NULL;
110 
111  e->list = list;
112  e->func = func;
113  e->uctx = UNCONST(void *, uctx);
114  e->file = file;
115  e->line = line;
116 
117  ATEXIT_DEBUG("%s - Thread %u arming %p/%p func=%p, uctx=%p (alloced %s:%u)",
118  __FUNCTION__, (unsigned int)pthread_self(),
119  list, e, e->func, e->uctx, e->file, e->line);
120 
121  fr_dlist_insert_head(&list->head, e);
122  talloc_set_destructor(e, _atexit_entry_free);
123 
124  return e;
125 }
126 
127 /** Talloc destructor for freeing list elements in order
128  *
129  */
131 {
132  ATEXIT_DEBUG("%s - Freeing destructor list %p", __FUNCTION__, list);
133 
134  fr_dlist_talloc_free(&list->head); /* Free in order */
135  return 0;
136 }
137 
138 /** Free any thread-local exit handler lists that pthread_key failed to fre
139  *
140  */
141 static void _global_free(void)
142 {
143 #ifdef HAVE_PTHREADS
144  pthread_mutex_lock(&fr_atexit_global_mutex);
145 #endif
146 
147  fr_cond_assert_msg(!is_exiting, "Global free function called multiple times");
148  is_exiting = true;
149 
150 #ifdef HAVE_PTHREADS
151  pthread_mutex_unlock(&fr_atexit_global_mutex);
152  TALLOC_FREE(fr_atexit_threads); /* Forcefully cleanup any thread-specific memory */
153 #endif
154  TALLOC_FREE(fr_atexit_global);
155 }
156 
157 /** Setup the atexit handler, should be called at the start of a program's execution
158  *
159  */
161 {
162  if (fr_atexit_global) return 0;
163 
164  fr_atexit_global = talloc_zero(NULL, fr_atexit_list_t);
165  if (unlikely(!fr_atexit_global)) return -1;
166 
167  ATEXIT_DEBUG("%s - Alloced global destructor list %p", __FUNCTION__, fr_atexit_global);
168 
170  talloc_set_destructor(fr_atexit_global, _destructor_list_free);
171 
172 #ifdef HAVE_PTHREADS
173  fr_atexit_threads = talloc_zero(NULL, fr_atexit_list_t);
174  if (unlikely(!fr_atexit_threads)) return -1;
175 
176  ATEXIT_DEBUG("%s - Alloced threads destructor list %p", __FUNCTION__, fr_atexit_threads);
177 
178  fr_dlist_talloc_init(&fr_atexit_threads->head, fr_atexit_entry_t, entry);
179  talloc_set_destructor(fr_atexit_threads, _destructor_list_free);
180 #endif
181 
182  atexit(_global_free); /* Call all remaining destructors at process exit */
183 
184  return 0;
185 }
186 
187 #ifdef HAVE_PTHREADS
188 #define CHECK_GLOBAL_SETUP() \
189 do { \
190  int _ret = 0; \
191  pthread_mutex_lock(&fr_atexit_global_mutex); \
192  fr_cond_assert_msg(!is_exiting, "New atexit handlers should not be allocated whilst exiting"); \
193  if (!fr_atexit_global) _ret = fr_atexit_global_setup(); \
194  pthread_mutex_unlock(&fr_atexit_global_mutex); \
195  if (_ret < 0) return _ret; \
196 } while(0)
197 #else
198 #define CHECK_GLOBAL_SETUP() \
199 do { \
200  int _ret = 0; \
201  fr_cond_assert_msg(!is_exiting, "New atexit handlers should not be allocated whilst exiting"); \
202  if (!fr_atexit_global) _ret = fr_atexit_global_setup(); \
203  if (_ret < 0) return _ret; \
204 } while(0)
205 #endif
206 
207 /** Add a free function to be called when the process exits
208  *
209  */
210 int _atexit_global(char const *file, int line, fr_atexit_t func, void const *uctx)
211 {
213 
214  if (unlikely(atexit_entry_alloc(file, line, fr_atexit_global, func, uctx) == NULL)) return -1;
215 
216  return 0;
217 }
218 
219 /** Remove a specific global destructor (without executing it)
220  *
221  * @note This function's primary purpose is to help diagnose issues with destructors
222  * from within a debugger.
223  *
224  * @param[in] uctx_scope Only process entries where the func and scope both match.
225  * @param[in] func Entries matching this function will be disarmed.
226  * @param[in] uctx associated with the entry.
227  * @return How many global destructors were disarmed.
228  */
229 unsigned int fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
230 {
231  fr_atexit_entry_t *e = NULL;
232  unsigned int count = 0;
233 
234  while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
235  fr_atexit_entry_t *disarm;
236 
237  if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
238 
239  ATEXIT_DEBUG("%s - Disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
240  __FUNCTION__,
241  fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
242 
243  disarm = e;
245  talloc_set_destructor(disarm, NULL);
246  talloc_free(disarm);
247 
248  count++;
249  }
250 
251  return count;
252 }
253 
254 /** Remove all global destructors (without executing them)
255  *
256  * @note This function's primary purpose is to help diagnose issues with destructors
257  * from within a debugger.
258  */
260 {
261  fr_atexit_entry_t *e = NULL;
262 
263  if (!fr_atexit_global) return;
264 
265  while ((e = fr_dlist_pop_head(&fr_atexit_global->head))) {
266  ATEXIT_DEBUG("%s - Disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
267  __FUNCTION__,
268  fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
269 
270  talloc_set_destructor(e, NULL);
271  talloc_free(e);
272  }
273 }
274 
275 /** Cause all global free triggers to fire
276  *
277  * This is necessary when libraries (perl) register their own
278  * atexit handlers using the normal POSIX mechanism, and we need
279  * to ensure all our atexit handlers fire before so any global
280  * deinit is done explicitly by us.
281  *
282  * @return
283  * - >= 0 The number of atexit handlers triggered on success.
284  * - <0 the return code from any atexit handlers that returned an error.
285  */
287 {
288  fr_atexit_entry_t *e = NULL, *to_free;
289  unsigned int count = 0;
290 
291  /*
292  * Iterate over the list of thread local
293  * destructor lists running the
294  * destructors.
295  */
296  while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
297  ATEXIT_DEBUG("%s - Triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
298  __FUNCTION__,
299  fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
300 
301  count++;
302  to_free = e;
304  if (talloc_free(to_free) < 0) {
305  fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
306  " (alloced %s:%u)",
307  fr_atexit_global, to_free,
308  to_free->func, to_free->uctx,
309  to_free->file, to_free->line);
310  return -1;
311  }
312  }
313 
314  return count;
315 }
316 
317 /** Iterates through all thread local destructor lists, causing destructor to be triggered
318  *
319  * This should only be called by the main process not by threads.
320  *
321  * The main purpose of the function is to force cleanups at a specific time for problematic
322  * destructors.
323  *
324  * @param[in] uctx_scope Only process entries where the func and scope both match.
325  * @param[in] func Entries matching this function will be triggered.
326  * @param[in] uctx associated with the entry.
327  * @return
328  * - >= 0 The number of atexit handlers triggered on success.
329  * - <0 the return code from any atexit handlers that returned an error.
330  */
331 int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
332 {
333  fr_atexit_entry_t *e = NULL, *to_free;
334 #ifdef HAVE_PTHREADS
335  fr_atexit_entry_t *ee;
336  fr_atexit_list_t *list;
337 #endif
338  unsigned int count = 0;
339 
340  if (!fr_atexit_global) goto do_threads;
341 
342  /*
343  * Iterate over the global destructors
344  */
345  while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
346  if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
347 
348  ATEXIT_DEBUG("%s - Triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
349  __FUNCTION__,
350  fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
351 
352  count++;
353  to_free = e;
355  if (talloc_free(to_free) < 0) {
356  fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
357  " (alloced %s:%u)",
358  fr_atexit_global, to_free,
359  to_free->func, to_free->uctx,
360  to_free->file, to_free->line);
361  return -1;
362  }
363  }
364  e = NULL;
365 
366 do_threads:
367 #ifdef HAVE_PTHREADS
368  if (!fr_atexit_threads) return 0;
369 
370  /*
371  * Iterate over the list of thread local
372  * destructor lists running the
373  * destructors.
374  */
375  while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
376  if (!e->func) continue; /* thread already joined */
377 
378  list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
379  ee = NULL;
380  while ((ee = fr_dlist_next(&list->head, ee))) {
381  if ((ee->func != func) || ((ee->uctx != uctx) && uctx_scope)) continue;
382 
383  ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
384  __FUNCTION__,
385  (unsigned int)pthread_self(),
386  list, ee, ee->func, ee->uctx, ee->file, ee->line);
387 
388  count++;
389  to_free = ee;
390  ee = fr_dlist_remove(&list->head, ee);
391  if (talloc_free(to_free) < 0) {
392  fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
393  " (alloced %s:%u)",
394  list, to_free,
395  to_free->func, to_free->uctx,
396  to_free->file, to_free->line);
397  return -1;
398  }
399  }
400  }
401 #endif
402 
403  return count;
404 }
405 
406 
407 /** Return whether we're currently in the teardown phase
408  *
409  * When this function returns true no more thread local or global
410  * destructors can be added.
411  */
413 {
414 #ifdef HAVE_PTHREADS
415  bool save_is_exiting;
416 
417  pthread_mutex_lock(&fr_atexit_global_mutex);
418  save_is_exiting = is_exiting;
419  pthread_mutex_unlock(&fr_atexit_global_mutex);
420 
421  return save_is_exiting;
422 #else
423  return is_exiting;
424 #endif
425 }
426 
427 #ifdef HAVE_PTHREADS
428 /** Talloc destructor for freeing list elements in order
429  *
430  */
431 static int _thread_local_list_free(fr_atexit_list_t *list)
432 {
433  ATEXIT_DEBUG("%s - Freeing _Thread_local destructor list %p", __FUNCTION__, list);
434 
435  fr_dlist_talloc_free(&list->head); /* Free in order */
436  pthread_mutex_lock(&fr_atexit_global_mutex);
437  list->e->func = NULL; /* Disarm the global entry that'd free the thread-specific list */
438  pthread_mutex_unlock(&fr_atexit_global_mutex);
439  return 0;
440 }
441 
442 /** Run all the thread local destructors
443  *
444  * @param[in] list The thread-specific exit handler list.
445  */
446 static void _thread_local_pthread_free(void *list)
447 {
448  talloc_free(list);
449 }
450 
451 /** Run all the thread local destructors
452  *
453  * @param[in] list The thread-specific exit handler list.
454  */
455 static int _thread_local_free(void *list)
456 {
457  thread_is_exiting = true;
458  return talloc_free(list);
459 }
460 
461 /** Add a new destructor
462  *
463  * @return
464  * - 0 on success.
465  * - -1 on memory allocation failure;
466  */
467 int _fr_atexit_thread_local(char const *file, int line,
468  fr_atexit_t func, void const *uctx)
469 {
471 
472  /*
473  * Initialise the thread local list, just for pthread_exit().
474  */
475  if (!fr_atexit_thread_local) {
476  fr_atexit_list_t *list;
477 
478  /*
479  * Must be heap allocated, because thread local
480  * structures can be freed before the key
481  * destructor is run (depending on platform).
482  */
483  list = talloc_zero(NULL, fr_atexit_list_t);
484  if (unlikely(!list)) return -1;
485 
486  ATEXIT_DEBUG("%s - Thread %u alloced _Thread_local destructor list %p",
487  __FUNCTION__,
488  (unsigned int)pthread_self(), list);
489 
491  (void) pthread_key_create(&list->key, _thread_local_pthread_free);
492 
493  /*
494  * We need to pass in a pointer to the heap
495  * memory because, again, the thread local
496  * indirection table may have disappeared
497  * by the time the thread destructor is
498  * called.
499  */
500  (void) pthread_setspecific(list->key, list);
501  talloc_set_destructor(list, _thread_local_list_free);
502 
503  /*
504  * Add a destructor for the thread-local list
505  * The pthread based destructor will disarm
506  * this if it fires, but leave it enabled if
507  * it doesn't, thus ensuring the memory is
508  * *always* freed one way or another.
509  */
510  pthread_mutex_lock(&fr_atexit_global_mutex);
511  list->e = atexit_entry_alloc(file, line,
512  fr_atexit_threads,
513  _thread_local_free,
514  list);
515 
516  pthread_mutex_unlock(&fr_atexit_global_mutex);
517 
518  fr_atexit_thread_local = list;
519  }
520 
521  /*
522  * Now allocate the actual atexit handler entry
523  */
524  if (atexit_entry_alloc(file, line, fr_atexit_thread_local, func, uctx) == NULL) return -1;
525 
526  return 0;
527 }
528 
529 /** Remove a specific destructor for this thread (without executing them)
530  *
531  * @note This function's primary purpose is to help diagnose issues with destructors
532  * from within a debugger.
533  *
534  * @param[in] uctx_scope Only process entries where the func and scope both match.
535  * @param[in] func Entries matching this function will be disarmed.
536  * @param[in] uctx associated with the entry.
537  * @return How many destructors were disarmed.
538  */
539 unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
540 {
541  fr_atexit_entry_t *e = NULL;
542  unsigned int count = 0;
543 
544  if (!fr_atexit_thread_local) return -1;
545 
546  while ((e = fr_dlist_next(&fr_atexit_thread_local->head, e))) {
547  fr_atexit_entry_t *disarm;
548 
549  if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
550 
551  ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
552  __FUNCTION__,
553  (unsigned int)pthread_self(),
554  fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
555  disarm = e;
557  talloc_set_destructor(disarm, NULL);
558  talloc_free(disarm);
559 
560  count++;
561  }
562 
563  return count;
564 }
565 
566 /** Remove all destructors for this thread (without executing them)
567  *
568  * @note This function's primary purpose is to help diagnose issues with destructors
569  * from within a debugger.
570  */
572 {
573  fr_atexit_entry_t *e = NULL;
574 
575  if (!fr_atexit_thread_local) return;
576 
577  while ((e = fr_dlist_pop_head(&fr_atexit_thread_local->head))) {
578  ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
579  __FUNCTION__,
580  (unsigned int)pthread_self(),
581  fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
582  talloc_set_destructor(e, NULL);
583  talloc_free(e);
584  }
585 }
586 
587 /** Cause all thread local free triggers to fire
588  *
589  * This is necessary when we're running in single threaded mode
590  * to ensure all "thread-local" memory (which isn't actually thread local)
591  * is cleaned up.
592  *
593  * One example is the OpenSSL log BIOs which must be cleaned up
594  * before fr_openssl_free is called.
595  *
596  * @return
597  * - >= 0 The number of atexit handlers triggered on success.
598  * - <0 the return code from any atexit handlers that returned an error.
599  */
601 {
602  fr_atexit_entry_t *e = NULL, *ee, *to_free;
603  fr_atexit_list_t *list;
604  unsigned int count = 0;
605 
606  /*
607  * Iterate over the list of thread local
608  * destructor lists running the
609  * destructors.
610  */
611  while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
612  if (!e->func) continue; /* thread already joined */
613 
614  list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
615  ee = NULL;
616  while ((ee = fr_dlist_next(&list->head, ee))) {
617  ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
618  __FUNCTION__,
619  (unsigned int)pthread_self(),
620  list, ee, ee->func, ee->uctx, ee->file, ee->line);
621 
622  count++;
623  to_free = ee;
624  ee = fr_dlist_remove(&list->head, ee);
625  if (talloc_free(to_free) < 0) {
626  fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
627  " (alloced %s:%u)",
628  list, to_free,
629  to_free->func, to_free->uctx,
630  to_free->file, to_free->line
631  );
632  return -1;
633  }
634  }
635  }
636 
637  return count;
638 }
639 
640 /** Return whether the thread is currently being cleaned up
641  *
642  */
643 bool fr_atexit_thread_is_exiting(void)
644 {
645  return thread_is_exiting;
646 }
647 #endif
int const char * file
Definition: acutest.h:702
int const char int line
Definition: acutest.h:702
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:229
static fr_atexit_list_t * fr_atexit_global
Definition: atexit.c:76
char const * file
File where this exit handler was added.
Definition: atexit.c:55
#define CHECK_GLOBAL_SETUP()
Definition: atexit.c:198
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition: atexit.c:160
fr_dlist_t entry
Entry in the handler dlist.
Definition: atexit.c:49
static _Thread_local bool thread_is_exiting
Definition: atexit.c:78
pthread_key_t key
Key used to trigger thread local destructors.
Definition: atexit.c:65
fr_atexit_t func
Function to call.
Definition: atexit.c:52
#define ATEXIT_DEBUG(...)
Definition: atexit.c:40
fr_dlist_head_t head
Head of the list of destructors.
Definition: atexit.c:63
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:102
void fr_atexit_global_disarm_all(void)
Remove all global destructors (without executing them)
Definition: atexit.c:259
fr_atexit_entry_t * e
Inserted into the global exit handler list to ensure this memory is cleaned up.
Definition: atexit.c:66
static int _atexit_entry_free(fr_atexit_entry_t *e)
Call the exit handler.
Definition: atexit.c:83
static bool is_exiting
Definition: atexit.c:77
void * uctx
uctx to pass.
Definition: atexit.c:53
int line
Line where this exit handler was added.
Definition: atexit.c:56
static void _global_free(void)
Free any thread-local exit handler lists that pthread_key failed to fre.
Definition: atexit.c:141
bool fr_atexit_is_exiting(void)
Return whether we're currently in the teardown phase.
Definition: atexit.c:412
static int _destructor_list_free(fr_atexit_list_t *list)
Talloc destructor for freeing list elements in order.
Definition: atexit.c:130
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition: atexit.c:286
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:331
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:210
fr_atexit_list_t * list
List this entry is in.
Definition: atexit.c:50
Entry in exit handler list.
Definition: atexit.c:48
Head of a list of exit handlers.
Definition: atexit.c:62
int(* fr_atexit_t)(void *uctx)
Destructor callback.
Definition: atexit.h:45
#define fr_atexit_thread_trigger_all(...)
Definition: atexit.h:233
#define fr_atexit_thread_local_disarm(...)
Definition: atexit.h:231
#define fr_atexit_thread_local_disarm_all(...)
Definition: atexit.h:232
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition: atexit.h:221
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:154
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition: dlist.h:672
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:555
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:163
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition: dlist.h:908
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:275
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:338
Head of a doubly linked list.
Definition: dlist.h:51
Entry in a doubly linked list.
Definition: dlist.h:41
talloc_free(reap)
return count
Definition: module.c:175
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition: strerror.h:84