The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 725c94ff00ea2c69d0031b064024a2766c0a4231 $")
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 */
48typedef 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
71static _Thread_local fr_atexit_list_t *fr_atexit_thread_local = NULL;
72static fr_atexit_list_t *fr_atexit_threads = NULL;
73static pthread_mutex_t fr_atexit_global_mutex = PTHREAD_MUTEX_INITIALIZER;
74#endif
75
77static bool is_exiting;
78static _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:%d)",
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:%d)",
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 */
141static 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() \
189do { \
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() \
199do { \
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 */
210int _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 */
229unsigned 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:%d)",
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:%d)",
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:%d)",
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:%d)",
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 */
331int 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
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:%d)",
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:%d)",
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
366do_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:%d)",
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:%d)",
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 */
431static 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 */
446static 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 */
455static 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 */
467int _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 */
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
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 */
539unsigned 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:%d)",
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:%d)",
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:%d)",
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:%d)",
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 */
643bool 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:167
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:156
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
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_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:672
#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
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
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:163
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84