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: 0a43d8a321f2cb51eef2a79e45a136a08974e52e $")
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
71
73
74#ifdef HAVE_PTHREADS
75/*
76 * Because GCC only added support in 2013 *sigh*
77 */
78#ifdef TLS_STORAGE_CLASS
79# define _Thread_local TLS_STORAGE_CLASS
80#endif
81
82/*
83 * Stop complaints of...:
84 *
85 * error: the address of 'x' will always evaluate as 'true' [-Werror=address]
86 *
87 * ...when functions are tested directly in the fr_atexit_global_once macro
88 */
89static inline int _fr_atexit_global_once_funcs(fr_atexit_t init_func, fr_atexit_t free_func, void *uctx)
90{
91 if (init_func) if (init_func(uctx) < 0) return -1;
92 if (free_func) fr_atexit_global(free_func, uctx);
93
94 return 0;
95}
96
97/** A generic function to free talloc chunks. Compatible with the fr_atexit_t type
98 *
99 * @param[in] to_free talloc chunk to free.
100 * @return the return code returned by talloc_free.
101 */
102static inline int fr_atexit_talloc_free(void *to_free)
103{
104 return talloc_free(to_free);
105}
106
107/** Setup pair of global init/free functions, returning errors from the specified init function
108 *
109 * Simplifies setting up data structures the first time a given function
110 * is called.
111 *
112 * Should be used in the body of the function before any initialisation
113 * dependent code.
114 *
115 * Will not share init status outside of the function.
116 *
117 * @param[out] _ret A pointer to where to write the result
118 * of the init function if called.
119 * @param[in] _init function to call. Will be called once
120 * during the process lifetime.
121 * May be NULL.
122 * @param[in] _free function to call. Will be called once
123 * at exit.
124 * May be NULL.
125 * Pass fr_atexit_talloc_free if _uctx is
126 * just a talloc chunk and no special logic
127 * is needed.
128 * @param[in] _uctx data to be passed to free function.
129 */
130#define fr_atexit_global_once_ret(_ret, _init, _free, _uctx) \
131do { \
132 static atomic_bool _init_done = false; \
133 static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER; \
134 void *_our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
135 *(_ret) = 0; \
136 if (unlikely(!atomic_load(&_init_done))) { \
137 pthread_mutex_lock(&_init_mutex); \
138 if (!atomic_load(&_init_done)) { \
139 if (_fr_atexit_global_once_funcs(_init, _free, _our_uctx) < 0) { \
140 *(_ret) = -1; \
141 } \
142 atomic_store(&_init_done, true); \
143 } \
144 pthread_mutex_unlock(&_init_mutex); \
145 } \
146} while (0)
147
148/** Setup pair of global init/free functions
149 *
150 * Simplifies setting up data structures the first time a given function
151 * is called.
152 *
153 * Should be used in the body of the function before any initialisation
154 * dependent code.
155 *
156 * Will not share init status outside of the function.
157 *
158 * @param[in] _init function to call. Will be called once
159 * during the process lifetime.
160 * May be NULL.
161 * @param[in] _free function to call. Will be called once
162 * at exit.
163 * May be NULL.
164 * Pass fr_atexit_talloc_free if _uctx is
165 * just a talloc chunk and no special logic
166 * is needed.
167 * @param[in] _uctx data to be passed to free function.
168 */
169#define fr_atexit_global_once(_init, _free, _uctx) \
170 fr_atexit_global_once_ret(&(int){ 0 }, _init, _free, _uctx)
171
172/** Set a destructor for thread local storage to free the memory on thread exit
173 *
174 * @note Pointers to thread local storage seem to become unusable as threads are
175 * destroyed. So we need to store the address of the memory to free, not
176 * the address of the thread local variable.
177 *
178 * @param[in] _name Name of variable e.g. 'my_tls'.
179 * @param[in] _free Destructor, called when the thread exits to clean up any data.
180 * Pass fr_atexit_talloc_free if _uctx is
181 * just a talloc chunk and no special logic
182 * is needed.
183 * @param[in] _uctx Memory to free.
184 */
185# define fr_atexit_thread_local(_name, _free, _uctx) \
186do { \
187 void *_our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
188 _fr_atexit_thread_local(__FILE__, __LINE__, _free, _our_uctx); \
189 _name = _our_uctx; \
190} while (0)
191
192int _fr_atexit_thread_local(char const *file, int line,
193 fr_atexit_t func, void const *uctx);
194
195unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx);
196
198
200
201bool fr_atexit_thread_is_exiting(void);
202
203/*
204 * If we're building without threading support,
205 * all this becomes much easier, and we just map
206 * all thread local cleanup entries to the global
207 * list.
208 */
209#else
210/*
211 * Don't emit a _Thread_local_storage qualifier
212 */
213# define __Thread_local
214# define fr_atexit_global_once(_init, _free, _uctx) \
215do { \
216 static bool _init_done = false; \
217 void * _our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
218 if (unlikely(!_init_done)) { \
219 _init(_our_uctx); \
220 fr_atexit_global(_free, _our_uctx); \
221 _init_done = true; \
222 } \
223} while(0);
224# define fr_atexit_thread_local(_name, _free, _uctx) \
225do { \
226 static bool _init_done = false; \
227 void * _our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
228 if (unlikely(!_init_done)) { \
229 fr_atexit_global(_free, _our_uctx); \
230 _init_done = true; \
231 } \
232 _name = _our_uctx; \
233} while(0);
234# define fr_atexit_thread_local_disarm(...) fr_atexit_global_disarm(__VA_ARGS__)
235# define fr_atexit_thread_local_disarm_all(...) fr_atexit_global_disarm_all(__VA_ARGS__)
236# define fr_atexit_thread_trigger_all(...)
237#endif
238
239#ifdef __cplusplus
240}
241#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:442
int(* fr_atexit_t)(void *uctx)
Destructor callback.
Definition atexit.h:44
void fr_atexit_thread_local_disable_alloc(void)
Disable lazy allocation of thread-local caches for the rest of the process.
Definition atexit.c:434
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:248
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:236
#define fr_atexit_thread_local_disarm(...)
Definition atexit.h:234
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
void fr_atexit_global_disarm_all(void)
Remove all global destructors (without executing them)
Definition atexit.c:278
#define fr_atexit_thread_local_disarm_all(...)
Definition atexit.h:235
#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:452
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:305
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:350
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:229
#define RCSIDH(h, id)
Definition build.h:513
talloc_free(hp)