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