int parallel = 1;
rc_request_t *this;
int force_af = AF_UNSPEC;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
/*
* It's easier having two sets of flags to set the
fr_debug_lvl = 0;
fr_log_fp = stdout;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
fr_perror("radclient");
bool found = false;
bool export = false;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
+
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
#ifndef NDEBUG
fr_time_delta_t exit_after = 0;
#endif
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
/*
* Setup talloc callbacks so we get useful errors
int c;
char const *raddb_dir = RADDBDIR;
char const *dict_dir = DICTDIR;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
rs_stats_t *stats;
fr_debug_lvl = 1;
fr_log_fp = stdout;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+
/*
* Useful if using radsniff as a long running stats daemon
*/
radsnmp_conf_t *conf;
int ret;
int sockfd;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
conf = talloc_zero(autofree, radsnmp_conf_t);
conf->proto = IPPROTO_UDP;
conf->timeout = fr_time_delta_from_sec(3);
conf->retries = 5;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
fr_perror("radsnmp");
uint32_t nas_ip_address = INADDR_NONE;
int zap = 0;
fr_dict_t *dict = NULL;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
char const *p;
main_config_t *config;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
int *inst = &c;
CONF_SECTION *cs;
int ret = EXIT_SUCCESS;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
dl_module_loader_t *dl_modules = NULL;
bool exit_now = false;
bool do_commands = false;
bool do_usage = false;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
fr_perror("unit_test_attribute");
.allow_foreign = true /* Because we don't know what protocol we're operating with */
};
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
config = main_config_alloc(NULL);
if (!config) {
fprintf(stderr, "Failed allocating main config");
fr_dict_t *dict = NULL;
char const *receipt_file = NULL;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
+
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
fr_dict_t *dict = NULL;
char const *receipt_file = NULL;
- TALLOC_CTX *autofree = talloc_autofree_context();
- TALLOC_CTX *thread_ctx = talloc_new(autofree);
+ TALLOC_CTX *autofree;
+ TALLOC_CTX *thread_ctx;
char *p;
main_config_t *config;
dl_module_loader_t *dl_modules = NULL;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+ thread_ctx = talloc_new(autofree);
+
config = main_config_alloc(autofree);
if (!config) {
fr_perror("unit_test_module");
_Thread_local fr_exit_handler_list_t *thread_local_atexit = NULL;
static fr_exit_handler_list_t *global_atexit = NULL;
static pthread_mutex_t global_atexit_mutex = PTHREAD_MUTEX_INITIALIZER;
+static bool is_exiting;
/** Call the exit handler
*/
static void _global_free(void)
{
+ pthread_mutex_lock(&global_atexit_mutex);
+ fr_cond_assert_msg(!is_exiting, "Global free function called multiple times");
+ is_exiting = true;
+ pthread_mutex_unlock(&global_atexit_mutex);
+
TALLOC_FREE(global_atexit);
}
+/** Setup the atexit handler, should be called at the start of a program's execution
+ *
+ */
+int fr_thread_local_atexit_setup(void)
+{
+ if (global_atexit) return 0;
+
+ global_atexit = talloc_zero(NULL, fr_exit_handler_list_t);
+ if (unlikely(!global_atexit)) return -1;
+
+ fr_dlist_talloc_init(&global_atexit->head, fr_exit_handler_entry_t, entry);
+ talloc_set_destructor(global_atexit, _global_list_free);
+ atexit(_global_free); /* Call all remaining destructors at process exit */
+
+ return 0;
+}
+
/** Add a new destructor
*
* @return
*/
int fr_thread_local_atexit(fr_thread_local_atexit_t func, void const *uctx)
{
+ int ret = 0;
+
/*
* Initialise the global list containing all the thread-local
* dlist destructors.
*/
pthread_mutex_lock(&global_atexit_mutex);
- if (!global_atexit) {
- global_atexit = talloc_zero(NULL, fr_exit_handler_list_t);
- if (unlikely(!global_atexit)) return -1;
-
- fr_dlist_talloc_init(&global_atexit->head, fr_exit_handler_entry_t, entry);
- talloc_set_destructor(global_atexit, _global_list_free);
- atexit(_global_free); /* Call all remaining destructors at process exit */
- }
+ fr_cond_assert_msg(!is_exiting, "New atexit handlers should not be allocated whilst exiting");
+ if (!global_atexit) ret = fr_thread_local_atexit_setup();
pthread_mutex_unlock(&global_atexit_mutex);
+ if (ret < 0) return ret;
/*
* Initialise the thread local list, just for pthread_exit().
typedef void(*fr_thread_local_atexit_t)(void *uctx);
+int fr_thread_local_atexit_setup(void);
+
int fr_thread_local_atexit(fr_thread_local_atexit_t func, void const *uctx);
/** Set a destructor for thread local storage to free the memory on thread exit
char history_file[PATH_MAX];
#endif
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
char *commands[MAX_COMMANDS];
int num_commands = -1;
char const *prompt = "radmin> ";
char prompt_buffer[1024];
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+
#ifndef NDEBUG
if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
fr_perror("radmin");
RADIUS_PACKET *packet = NULL;
RADIUS_PACKET *reply = NULL;
- TALLOC_CTX *autofree = talloc_autofree_context();
+ TALLOC_CTX *autofree;
int ret;
+ /*
+ * Must be called first, so the handler is called last
+ */
+ fr_thread_local_atexit_setup();
+
+ autofree = talloc_autofree_context();
+
fr_debug_lvl = 1;
while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != -1) switch(c) {