The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
atexit.h
Go to the documentation of this file.
1#pragma once
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16 */
17
18/** Functions to help with cleanup
19 *
20 * Simplifies cleaning up thread local and global resources
21 *
22 * @file lib/util/atexit.h
23 *
24 * @copyright 2020-2022 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 * @copyright 2013-2016 The FreeRADIUS server project
26 */
27RCSIDH(atexit_h, "$Id: 6bff98f54c5da57ff98267e112dbb6c310d76b42 $")
28
29#include <stdatomic.h>
30#include <pthread.h>
31#include <freeradius-devel/util/talloc.h>
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
37/** Destructor callback
38 *
39 * @param[in] uctx to free.
40 * @return
41 * - 0 on success.
42 * - -1 on failure.
43 */
44typedef int(*fr_atexit_t)(void *uctx);
45
47
48int _atexit_global(char const *file, int line, fr_atexit_t func, void const *uctx);
49
50/** Add a free function to the global free list
51 *
52 * @param[in] _func to call.
53 * @param[in] _uctx to pass to func.
54 * @return
55 * - 0 on success.
56 * - -1 on failure.
57 */
58#define fr_atexit_global(_func, _uctx) _atexit_global(__FILE__, __LINE__, _func, _uctx)
59
60unsigned int fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx);
61
63
65
66int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx);
67
68bool fr_atexit_is_exiting(void);
69
70#ifdef HAVE_PTHREADS
71/*
72 * Because GCC only added support in 2013 *sigh*
73 */
74#ifdef TLS_STORAGE_CLASS
75# define _Thread_local TLS_STORAGE_CLASS
76#endif
77
78/*
79 * Stop complaints of...:
80 *
81 * error: the address of 'x' will always evaluate as 'true' [-Werror=address]
82 *
83 * ...when functions are tested directly in the fr_atexit_global_once macro
84 */
85static inline int _fr_atexit_global_once_funcs(fr_atexit_t init_func, fr_atexit_t free_func, void *uctx)
86{
87 if (init_func) if (init_func(uctx) < 0) return -1;
88 if (free_func) fr_atexit_global(free_func, uctx);
89
90 return 0;
91}
92
93/** A generic function to free talloc chunks. Compatible with the fr_atexit_t type
94 *
95 * @param[in] to_free talloc chunk to free.
96 * @return the return code returned by talloc_free.
97 */
98static inline int fr_atexit_talloc_free(void *to_free)
99{
100 return talloc_free(to_free);
101}
102
103/** Setup pair of global init/free functions, returning errors from the specified init function
104 *
105 * Simplifies setting up data structures the first time a given function
106 * is called.
107 *
108 * Should be used in the body of the function before any initialisation
109 * dependent code.
110 *
111 * Will not share init status outside of the function.
112 *
113 * @param[out] _ret A pointer to where to write the result
114 * of the init function if called.
115 * @param[in] _init function to call. Will be called once
116 * during the process lifetime.
117 * May be NULL.
118 * @param[in] _free function to call. Will be called once
119 * at exit.
120 * May be NULL.
121 * Pass fr_atexit_talloc_free if _uctx is
122 * just a talloc chunk and no special logic
123 * is needed.
124 * @param[in] _uctx data to be passed to free function.
125 */
126#define fr_atexit_global_once_ret(_ret, _init, _free, _uctx) \
127do { \
128 static atomic_bool _init_done = false; \
129 static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER; \
130 void *_our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
131 *(_ret) = 0; \
132 if (unlikely(!atomic_load(&_init_done))) { \
133 pthread_mutex_lock(&_init_mutex); \
134 if (!atomic_load(&_init_done)) { \
135 if (_fr_atexit_global_once_funcs(_init, _free, _our_uctx) < 0) { \
136 *(_ret) = -1; \
137 } \
138 atomic_store(&_init_done, true); \
139 } \
140 pthread_mutex_unlock(&_init_mutex); \
141 } \
142} while (0)
143
144/** Setup pair of global init/free functions
145 *
146 * Simplifies setting up data structures the first time a given function
147 * is called.
148 *
149 * Should be used in the body of the function before any initialisation
150 * dependent code.
151 *
152 * Will not share init status outside of the function.
153 *
154 * @param[in] _init function to call. Will be called once
155 * during the process lifetime.
156 * May be NULL.
157 * @param[in] _free function to call. Will be called once
158 * at exit.
159 * May be NULL.
160 * Pass fr_atexit_talloc_free if _uctx is
161 * just a talloc chunk and no special logic
162 * is needed.
163 * @param[in] _uctx data to be passed to free function.
164 */
165#define fr_atexit_global_once(_init, _free, _uctx) \
166 fr_atexit_global_once_ret(&(int){ 0 }, _init, _free, _uctx)
167
168/** Set a destructor for thread local storage to free the memory on thread exit
169 *
170 * @note Pointers to thread local storage seem to become unusable as threads are
171 * destroyed. So we need to store the address of the memory to free, not
172 * the address of the thread local variable.
173 *
174 * @param[in] _name Name of variable e.g. 'my_tls'.
175 * @param[in] _free Destructor, called when the thread exits to clean up any data.
176 * Pass fr_atexit_talloc_free if _uctx is
177 * just a talloc chunk and no special logic
178 * is needed.
179 * @param[in] _uctx Memory to free.
180 */
181# define fr_atexit_thread_local(_name, _free, _uctx) \
182do { \
183 void *_our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
184 _fr_atexit_thread_local(__FILE__, __LINE__, _free, _our_uctx); \
185 _name = _our_uctx; \
186} while (0)
187
188int _fr_atexit_thread_local(char const *file, int line,
189 fr_atexit_t func, void const *uctx);
190
191unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx);
192
194
196
197bool fr_atexit_thread_is_exiting(void);
198
199/*
200 * If we're building without threading support,
201 * all this becomes much easier, and we just map
202 * all thread local cleanup entries to the global
203 * list.
204 */
205#else
206/*
207 * Don't emit a _Thread_local_storage qualifier
208 */
209# define __Thread_local
210# define fr_atexit_global_once(_init, _free, _uctx) \
211do { \
212 static bool _init_done = false; \
213 void * _our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
214 if (unlikely(!_init_done)) { \
215 _init(_our_uctx); \
216 fr_atexit_global(_free, _our_uctx); \
217 _init_done = true; \
218 } \
219} while(0);
220# define fr_atexit_thread_local(_name, _free, _uctx) \
221do { \
222 static bool _init_done = false; \
223 void * _our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
224 if (unlikely(!_init_done)) { \
225 fr_atexit_global(_free, _our_uctx); \
226 _init_done = true; \
227 } \
228 _name = _our_uctx; \
229} while(0);
230# define fr_atexit_thread_local_disarm(...) fr_atexit_global_disarm(__VA_ARGS__)
231# define fr_atexit_thread_local_disarm_all(...) fr_atexit_global_disarm_all(__VA_ARGS__)
232# define fr_atexit_thread_trigger_all(...)
233#endif
234
235#ifdef __cplusplus
236}
237#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
int(* fr_atexit_t)(void *uctx)
Destructor callback.
Definition atexit.h:44
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
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:232
#define fr_atexit_thread_local_disarm(...)
Definition atexit.h:230
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
void fr_atexit_global_disarm_all(void)
Remove all global destructors (without executing them)
Definition atexit.c:258
#define fr_atexit_thread_local_disarm_all(...)
Definition atexit.h:231
#define fr_atexit_global(_func, _uctx)
Add a free function to the global free list.
Definition atexit.h:58
bool fr_atexit_is_exiting(void)
Return whether we're currently in the teardown phase.
Definition atexit.c:411
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
#define RCSIDH(h, id)
Definition build.h:507
talloc_free(hp)