]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Rework the atexit code to function without pthreads
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 28 Jun 2022 18:40:45 +0000 (13:40 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 28 Jun 2022 20:24:57 +0000 (15:24 -0500)
src/include/build.h
src/lib/util/atexit.c
src/lib/util/atexit.h
src/lib/util/dl.c

index 0b96f19bf00f5bbda4a9eaa09156e6ef4fc228e7..d139d5d56e3c320ac01a29c6b145c8e1a92504d3 100644 (file)
@@ -65,6 +65,18 @@ extern "C" {
 #include <stddef.h>
 #include <string.h>
 
+/*
+ *     These are compile time options to toggle whether
+ *     we're building with thread support.
+ *
+ *     With EMSCRIPTEN threading support isn't guaranteed
+ *     as many browsers have explicitly disabled support
+ *     due to spectre attacks.
+ */
+#if (defined(__EMSCRIPTEN__) && defined(__EMSCRIPTEN_PTHREADS__)) || !defined(__EMSCRIPTEN__) && defined(HAVE_PTHREADS_H)
+#  define HAVE_PTHREADS 1
+#endif
+
 /*
  *     GCC will sometimes define "unix" as well as "__unix",
  *     which gets confusing and is unnecessary.
index f7745dd44742a3a7a2d1b0fa28f4ee75bd198002..2e032bb174f526d8266bdc18c392442c8791fb9c 100644 (file)
@@ -30,8 +30,9 @@ RCSID("$Id$")
 #include <freeradius-devel/util/dlist.h>
 #include <freeradius-devel/util/atexit.h>
 
-
+#ifdef HAVE_PTHREADS
 #include <pthread.h>
+#endif
 
 #if defined(DEBUG_ATEXIT) && !defined(NDEBUG)
 #  define ATEXIT_DEBUG         FR_FAULT_LOG
@@ -68,12 +69,14 @@ struct fr_exit_handler_list_s {
                                                        ///< to ensure this memory is cleaned up.
 };
 
+#ifdef HAVE_PTHREADS
 static _Thread_local fr_atexit_list_t  *fr_atexit_thread_local = NULL;
 static fr_atexit_list_t                        *fr_atexit_threads = NULL;
-static fr_atexit_list_t                        *fr_atexit_global = NULL;
 static pthread_mutex_t                 fr_atexit_global_mutex = PTHREAD_MUTEX_INITIALIZER;
-static bool                            is_exiting;
+#endif
 
+static fr_atexit_list_t                        *fr_atexit_global = NULL;
+static bool                            is_exiting;
 
 /** Call the exit handler
  *
@@ -125,36 +128,6 @@ static fr_atexit_entry_t *atexit_entry_alloc(NDEBUG_LOCATION_ARGS
        return e;
 }
 
-/** Talloc destructor for freeing list elements in order
- *
- */
-static int _thread_local_list_free(fr_atexit_list_t *list)
-{
-       ATEXIT_DEBUG("%s - Freeing _Thread_local destructor list %p",  __FUNCTION__, list);
-
-       fr_dlist_talloc_free(&list->head);      /* Free in order */
-       list->e->func = NULL;                   /* Disarm the global entry that'd free the thread-specific list */
-       return 0;
-}
-
-/** Run all the thread local destructors
- *
- * @param[in] list     The thread-specific exit handler list.
- */
-static void _thread_local_pthread_free(void *list)
-{
-       talloc_free(list);
-}
-
-/** Run all the thread local destructors
- *
- * @param[in] list     The thread-specific exit handler list.
- */
-static int _thread_local_free(void *list)
-{
-       return talloc_free(list);
-}
-
 /** Talloc destructor for freeing list elements in order
  *
  */
@@ -171,12 +144,17 @@ static int _destructor_list_free(fr_atexit_list_t *list)
  */
 static void _global_free(void)
 {
+#ifdef HAVE_PTHREADS
        pthread_mutex_lock(&fr_atexit_global_mutex);
+#endif
+
        fr_cond_assert_msg(!is_exiting, "Global free function called multiple times");
        is_exiting = true;
-       pthread_mutex_unlock(&fr_atexit_global_mutex);
 
+#ifdef HAVE_PTHREADS
+       pthread_mutex_unlock(&fr_atexit_global_mutex);
        TALLOC_FREE(fr_atexit_threads); /* Forcefully cleanup any thread-specific memory */
+#endif
        TALLOC_FREE(fr_atexit_global);
 }
 
@@ -195,6 +173,7 @@ int fr_atexit_global_setup(void)
        fr_dlist_talloc_init(&fr_atexit_global->head, fr_atexit_entry_t, entry);
        talloc_set_destructor(fr_atexit_global, _destructor_list_free);
 
+#ifdef HAVE_PTHREADS
        fr_atexit_threads = talloc_zero(NULL, fr_atexit_list_t);
        if (unlikely(!fr_atexit_threads)) return -1;
 
@@ -202,12 +181,14 @@ int fr_atexit_global_setup(void)
 
        fr_dlist_talloc_init(&fr_atexit_threads->head, fr_atexit_entry_t, entry);
        talloc_set_destructor(fr_atexit_threads, _destructor_list_free);
+#endif
 
        atexit(_global_free);   /* Call all remaining destructors at process exit */
 
        return 0;
 }
 
+#ifdef HAVE_PTHREADS
 #define CHECK_GLOBAL_SETUP() \
 do { \
        int _ret = 0; \
@@ -217,6 +198,15 @@ do { \
        pthread_mutex_unlock(&fr_atexit_global_mutex); \
        if (_ret < 0) return _ret; \
 } while(0)
+#else
+#define CHECK_GLOBAL_SETUP() \
+do { \
+       int _ret = 0; \
+       fr_cond_assert_msg(!is_exiting, "New atexit handlers should not be allocated whilst exiting"); \
+       if (!fr_atexit_global) _ret = fr_atexit_global_setup(); \
+       if (_ret < 0) return _ret; \
+} while(0)
+#endif
 
 /** Add a free function to be called when the process exits
  *
@@ -231,190 +221,6 @@ int _atexit_global(NDEBUG_LOCATION_ARGS
        return 0;
 }
 
-/** Add a new destructor
- *
- * @return
- *     - 0 on success.
- *      - -1 on memory allocation failure;
- */
-int _fr_atexit_thread_local(NDEBUG_LOCATION_ARGS
-                           fr_atexit_t func, void const *uctx)
-{
-       CHECK_GLOBAL_SETUP();
-
-       /*
-        *      Initialise the thread local list, just for pthread_exit().
-        */
-       if (!fr_atexit_thread_local) {
-               fr_atexit_list_t *list;
-
-               /*
-                *      Must be heap allocated, because thread local
-                *      structures can be freed before the key
-                *      destructor is run (depending on platform).
-                */
-               list = talloc_zero(NULL, fr_atexit_list_t);
-               if (unlikely(!list)) return -1;
-
-               ATEXIT_DEBUG("%s - Thread %u alloced _Thread_local destructor list %p",
-                            __FUNCTION__,
-                            (unsigned int)pthread_self(), list);
-
-               fr_dlist_talloc_init(&list->head, fr_atexit_entry_t, entry);
-               (void) pthread_key_create(&list->key, _thread_local_pthread_free);
-
-               /*
-                *      We need to pass in a pointer to the heap
-                *      memory because, again, the thread local
-                *      indirection table may have disappeared
-                *      by the time the thread destructor is
-                *      called.
-                */
-               (void) pthread_setspecific(list->key, list);
-               talloc_set_destructor(list, _thread_local_list_free);
-
-               /*
-                *      Add a destructor for the thread-local list
-                *      The pthread based destructor will disarm
-                *      this if it fires, but leave it enabled if
-                *      it doesn't, thus ensuring the memory is
-                *      *always* freed one way or another.
-                */
-               pthread_mutex_lock(&fr_atexit_global_mutex);
-               list->e = atexit_entry_alloc(NDEBUG_LOCATION_VALS
-                                            fr_atexit_threads,
-                                            _thread_local_free,
-                                            list);
-
-               pthread_mutex_unlock(&fr_atexit_global_mutex);
-
-               fr_atexit_thread_local = list;
-       }
-
-       /*
-        *      Now allocate the actual atexit handler entry
-        */
-       if (atexit_entry_alloc(NDEBUG_LOCATION_VALS fr_atexit_thread_local, func, uctx) == NULL) return -1;
-
-       return 0;
-}
-
-/** Remove a specific destructor for this thread (without executing them)
- *
- * @note This function's primary purpose is to help diagnose issues with destructors
- *      from within a debugger.
- *
- * @param[in] uctx_scope       Only process entries where the func and scope both match.
- * @param[in] func             Entries matching this function will be disarmed.
- * @param[in] uctx             associated with the entry.
- * @return How many destructors were disarmed.
- */
-unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
-{
-       fr_atexit_entry_t       *e = NULL;
-       unsigned int            count = 0;
-
-       if (!fr_atexit_thread_local) return -1;
-
-       while ((e = fr_dlist_next(&fr_atexit_thread_local->head, e))) {
-               fr_atexit_entry_t *disarm;
-
-               if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
-
-               ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
-                            __FUNCTION__,
-                            (unsigned int)pthread_self(),
-                            fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
-               disarm = e;
-               e = fr_dlist_remove(&fr_atexit_thread_local->head, e);
-               talloc_set_destructor(disarm, NULL);
-               talloc_free(disarm);
-
-               count++;
-       }
-
-       return count;
-}
-
-/** Remove all destructors for this thread (without executing them)
- *
- * @note This function's primary purpose is to help diagnose issues with destructors
- *      from within a debugger.
- */
-void fr_atexit_thread_local_disarm_all(void)
-{
-       fr_atexit_entry_t *e = NULL;
-
-       if (!fr_atexit_thread_local) return;
-
-       while ((e = fr_dlist_pop_head(&fr_atexit_thread_local->head))) {
-               ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
-                            __FUNCTION__,
-                            (unsigned int)pthread_self(),
-                            fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
-               talloc_set_destructor(e, NULL);
-               talloc_free(e);
-       }
-}
-
-/** Cause all thread local free triggers to fire
- *
- * This is necessary when we're running in single threaded mode
- * to ensure all "thread-local" memory (which isn't actually thread local)
- * is cleaned up.
- *
- * One example is the OpenSSL log BIOs which must be cleaned up
- * before fr_openssl_free is called.
- *
- * @return
- *      - >= 0 The number of atexit handlers triggered on success.
- *      - <0 the return code from any atexit handlers that returned an error.
- */
-int fr_atexit_thread_trigger_all(void)
-{
-       fr_atexit_entry_t               *e = NULL, *ee, *to_free;
-       fr_atexit_list_t                *list;
-       unsigned int                    count = 0;
-
-       /*
-        *      Iterate over the list of thread local
-        *      destructor lists running the
-        *      destructors.
-        */
-       while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
-               if (!e->func) continue; /* thread already joined */
-
-               list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
-               ee = NULL;
-               while ((ee = fr_dlist_next(&list->head, ee))) {
-                       ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
-                                    __FUNCTION__,
-                                    (unsigned int)pthread_self(),
-                                    list, ee, ee->func, ee->uctx, ee->file, ee->line);
-
-                       count++;
-                       to_free = ee;
-                       ee = fr_dlist_remove(&list->head, ee);
-                       if (talloc_free(to_free) < 0) {
-                               fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
-#ifndef NDEBUG
-                                                       " (alloced %s:%u)"
-#endif
-                                                       ,
-                                                       list, to_free,
-                                                       to_free->func, to_free->uctx
-#ifndef NDEBUG
-                                                       , to_free->file, to_free->line
-#endif
-                                                       );
-                               return -1;
-                       }
-               }
-       }
-
-       return count;
-}
-
 /** Remove a specific global destructor (without executing it)
  *
  * @note This function's primary purpose is to help diagnose issues with destructors
@@ -430,8 +236,6 @@ unsigned int fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void con
        fr_atexit_entry_t       *e = NULL;
        unsigned int            count = 0;
 
-       if (!fr_atexit_thread_local) return -1;
-
        while ((e = fr_dlist_next(&fr_atexit_global->head, e))) {
                fr_atexit_entry_t *disarm;
 
@@ -537,8 +341,11 @@ int fr_atexit_global_trigger_all(void)
  */
 int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
 {
-       fr_atexit_entry_t               *e = NULL, *ee, *to_free;
+       fr_atexit_entry_t               *e = NULL, *to_free;
+#ifdef HAVE_PTHREADS
+       fr_atexit_entry_t               *ee;
        fr_atexit_list_t                *list;
+#endif
        unsigned int                    count = 0;
 
        if (!fr_atexit_global) goto do_threads;
@@ -574,6 +381,7 @@ int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
        e = NULL;
 
 do_threads:
+#ifdef HAVE_PTHREADS
        if (!fr_atexit_threads) return 0;
 
        /*
@@ -613,6 +421,7 @@ do_threads:
                        }
                }
        }
+#endif
 
        return count;
 }
@@ -627,3 +436,219 @@ bool fr_atexit_is_exiting(void)
 {
        return is_exiting;
 }
+
+#ifdef HAVE_PTHREADS
+/** Talloc destructor for freeing list elements in order
+ *
+ */
+static int _thread_local_list_free(fr_atexit_list_t *list)
+{
+       ATEXIT_DEBUG("%s - Freeing _Thread_local destructor list %p",  __FUNCTION__, list);
+
+       fr_dlist_talloc_free(&list->head);      /* Free in order */
+       list->e->func = NULL;                   /* Disarm the global entry that'd free the thread-specific list */
+       return 0;
+}
+
+/** Run all the thread local destructors
+ *
+ * @param[in] list     The thread-specific exit handler list.
+ */
+static void _thread_local_pthread_free(void *list)
+{
+       talloc_free(list);
+}
+
+/** Run all the thread local destructors
+ *
+ * @param[in] list     The thread-specific exit handler list.
+ */
+static int _thread_local_free(void *list)
+{
+       return talloc_free(list);
+}
+
+/** Add a new destructor
+ *
+ * @return
+ *     - 0 on success.
+ *      - -1 on memory allocation failure;
+ */
+int _fr_atexit_thread_local(NDEBUG_LOCATION_ARGS
+                           fr_atexit_t func, void const *uctx)
+{
+       CHECK_GLOBAL_SETUP();
+
+       /*
+        *      Initialise the thread local list, just for pthread_exit().
+        */
+       if (!fr_atexit_thread_local) {
+               fr_atexit_list_t *list;
+
+               /*
+                *      Must be heap allocated, because thread local
+                *      structures can be freed before the key
+                *      destructor is run (depending on platform).
+                */
+               list = talloc_zero(NULL, fr_atexit_list_t);
+               if (unlikely(!list)) return -1;
+
+               ATEXIT_DEBUG("%s - Thread %u alloced _Thread_local destructor list %p",
+                            __FUNCTION__,
+                            (unsigned int)pthread_self(), list);
+
+               fr_dlist_talloc_init(&list->head, fr_atexit_entry_t, entry);
+               (void) pthread_key_create(&list->key, _thread_local_pthread_free);
+
+               /*
+                *      We need to pass in a pointer to the heap
+                *      memory because, again, the thread local
+                *      indirection table may have disappeared
+                *      by the time the thread destructor is
+                *      called.
+                */
+               (void) pthread_setspecific(list->key, list);
+               talloc_set_destructor(list, _thread_local_list_free);
+
+               /*
+                *      Add a destructor for the thread-local list
+                *      The pthread based destructor will disarm
+                *      this if it fires, but leave it enabled if
+                *      it doesn't, thus ensuring the memory is
+                *      *always* freed one way or another.
+                */
+               pthread_mutex_lock(&fr_atexit_global_mutex);
+               list->e = atexit_entry_alloc(NDEBUG_LOCATION_VALS
+                                            fr_atexit_threads,
+                                            _thread_local_free,
+                                            list);
+
+               pthread_mutex_unlock(&fr_atexit_global_mutex);
+
+               fr_atexit_thread_local = list;
+       }
+
+       /*
+        *      Now allocate the actual atexit handler entry
+        */
+       if (atexit_entry_alloc(NDEBUG_LOCATION_VALS fr_atexit_thread_local, func, uctx) == NULL) return -1;
+
+       return 0;
+}
+
+/** Remove a specific destructor for this thread (without executing them)
+ *
+ * @note This function's primary purpose is to help diagnose issues with destructors
+ *      from within a debugger.
+ *
+ * @param[in] uctx_scope       Only process entries where the func and scope both match.
+ * @param[in] func             Entries matching this function will be disarmed.
+ * @param[in] uctx             associated with the entry.
+ * @return How many destructors were disarmed.
+ */
+unsigned int fr_atexit_thread_local_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx)
+{
+       fr_atexit_entry_t       *e = NULL;
+       unsigned int            count = 0;
+
+       if (!fr_atexit_thread_local) return -1;
+
+       while ((e = fr_dlist_next(&fr_atexit_thread_local->head, e))) {
+               fr_atexit_entry_t *disarm;
+
+               if ((e->func != func) || ((e->uctx != uctx) && uctx_scope)) continue;
+
+               ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
+                            __FUNCTION__,
+                            (unsigned int)pthread_self(),
+                            fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
+               disarm = e;
+               e = fr_dlist_remove(&fr_atexit_thread_local->head, e);
+               talloc_set_destructor(disarm, NULL);
+               talloc_free(disarm);
+
+               count++;
+       }
+
+       return count;
+}
+
+/** Remove all destructors for this thread (without executing them)
+ *
+ * @note This function's primary purpose is to help diagnose issues with destructors
+ *      from within a debugger.
+ */
+void fr_atexit_thread_local_disarm_all(void)
+{
+       fr_atexit_entry_t *e = NULL;
+
+       if (!fr_atexit_thread_local) return;
+
+       while ((e = fr_dlist_pop_head(&fr_atexit_thread_local->head))) {
+               ATEXIT_DEBUG("%s - Thread %u disarming %p/%p func=%p, uctx=%p (alloced %s:%u)",
+                            __FUNCTION__,
+                            (unsigned int)pthread_self(),
+                            fr_atexit_thread_local, e, e->func, e->uctx, e->file, e->line);
+               talloc_set_destructor(e, NULL);
+               talloc_free(e);
+       }
+}
+
+/** Cause all thread local free triggers to fire
+ *
+ * This is necessary when we're running in single threaded mode
+ * to ensure all "thread-local" memory (which isn't actually thread local)
+ * is cleaned up.
+ *
+ * One example is the OpenSSL log BIOs which must be cleaned up
+ * before fr_openssl_free is called.
+ *
+ * @return
+ *      - >= 0 The number of atexit handlers triggered on success.
+ *      - <0 the return code from any atexit handlers that returned an error.
+ */
+int fr_atexit_thread_trigger_all(void)
+{
+       fr_atexit_entry_t               *e = NULL, *ee, *to_free;
+       fr_atexit_list_t                *list;
+       unsigned int                    count = 0;
+
+       /*
+        *      Iterate over the list of thread local
+        *      destructor lists running the
+        *      destructors.
+        */
+       while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
+               if (!e->func) continue; /* thread already joined */
+
+               list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
+               ee = NULL;
+               while ((ee = fr_dlist_next(&list->head, ee))) {
+                       ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
+                                    __FUNCTION__,
+                                    (unsigned int)pthread_self(),
+                                    list, ee, ee->func, ee->uctx, ee->file, ee->line);
+
+                       count++;
+                       to_free = ee;
+                       ee = fr_dlist_remove(&list->head, ee);
+                       if (talloc_free(to_free) < 0) {
+                               fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p"
+#ifndef NDEBUG
+                                                       " (alloced %s:%u)"
+#endif
+                                                       ,
+                                                       list, to_free,
+                                                       to_free->func, to_free->uctx
+#ifndef NDEBUG
+                                                       , to_free->file, to_free->line
+#endif
+                                                       );
+                               return -1;
+                       }
+               }
+       }
+
+       return count;
+}
+#endif
index 9d80f1ca25da9bcbdb330b72033db09e49e8e72e..17c9f9da629101393c9cd6a4184ecdba99a82814 100644 (file)
@@ -34,16 +34,12 @@ RCSIDH(atexit_h, "$Id$")
 extern "C" {
 #endif
 
-/*
- *     Because GCC only added support in 2013 *sigh*
- */
-#ifdef TLS_STORAGE_CLASS
-#  define _Thread_local TLS_STORAGE_CLASS
-#endif
-
 /** Destructor callback
  *
  * @param[in] uctx     to free.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
  */
 typedef int(*fr_atexit_t)(void *uctx);
 
@@ -59,8 +55,25 @@ int _atexit_global(NDEBUG_LOCATION_ARGS fr_atexit_t func, void const *uctx);
  *     - 0 on success.
  *      - -1 on failure.
  */
-#define fr_atexit_global(_func, _uctx) \
-       _atexit_global(NDEBUG_LOCATION_EXP _func, _uctx)
+#define fr_atexit_global(_func, _uctx) _atexit_global(NDEBUG_LOCATION_EXP _func, _uctx)
+
+unsigned int   fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx);
+
+void           fr_atexit_global_disarm_all(void);
+
+int            fr_atexit_global_trigger_all(void);
+
+int            fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx);
+
+bool           fr_atexit_is_exiting(void);
+
+#ifdef HAVE_PTHREADS
+/*
+ *     Because GCC only added support in 2013 *sigh*
+ */
+#ifdef TLS_STORAGE_CLASS
+#  define _Thread_local TLS_STORAGE_CLASS
+#endif
 
 /** Setup pair of global init/free functions
  *
@@ -82,17 +95,17 @@ int _atexit_global(NDEBUG_LOCATION_ARGS fr_atexit_t func, void const *uctx);
 { \
        static atomic_bool      _init_done = false; \
        static pthread_mutex_t  _init_mutex = PTHREAD_MUTEX_INITIALIZER; \
+       void *_our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
        if (unlikely(!atomic_load(&_init_done))) { \
                pthread_mutex_lock(&_init_mutex); \
                if (!atomic_load(&_init_done)) { \
-                       _init(_uctx); \
-                       fr_atexit_global(_free, _uctx); \
+                       _init(_our_uctx); \
+                       fr_atexit_global(_free, _our_uctx); \
                        atomic_store(&_init_done, true); \
                } \
                pthread_mutex_unlock(&_init_mutex); \
        } \
 }
-
 /** Set a destructor for thread local storage to free the memory on thread exit
  *
  * @note Pointers to thread local storage seem to become unusable as threads are
@@ -119,15 +132,41 @@ void              fr_atexit_thread_local_disarm_all(void);
 
 int            fr_atexit_thread_trigger_all(void);
 
-unsigned int   fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx);
-
-void           fr_atexit_global_disarm_all(void);
-
-int            fr_atexit_global_trigger_all(void);
-
-int            fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx);
-
-bool           fr_atexit_is_exiting(void);
+/*
+ *     If we're building without threading support,
+ *     all this becomes much easier, and we just map
+ *     all thread local cleanup entries to the global
+ *     list.
+ */
+#else
+/*
+ *     Don't emit a _Thread_local_storage qualifier
+ */
+#  define __Thread_local
+#  define fr_atexit_global_once(_init, _free, _uctx) \
+do { \
+       static bool _init_done = false; \
+       void * _our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
+       if (unlikely(!_init_done)) { \
+               _init(_our_uctx); \
+               fr_atexit_global(_free, _our_uctx); \
+               _init_done = true; \
+       } \
+} while(0);
+#  define fr_atexit_thread_local(_name, _free, _uctx) \
+do { \
+       static bool _init_done = false; \
+       void * _our_uctx = _uctx; /* stop _uctx being evaluated multiple times, it may be a call to malloc() */ \
+       if (unlikely(!_init_done)) { \
+               fr_atexit_global(_free, _our_uctx); \
+               _init_done = true; \
+       } \
+       _name = _our_uctx; \
+} while(0);
+#  define fr_atexit_thread_local_disarm(...)           fr_atexit_global_disarm(__VA_ARGS__)
+#  define fr_atexit_thread_local_disarm_all(...)       fr_atexit_global_disarm_all(__VA_ARGS__)
+#  define fr_atexit_thread_trigger_all(...)            fr_atexit_global_trigger_all(__VA_ARGS__)
+#endif
 
 #ifdef __cplusplus
 }
index 67078037ef071248ad96f16e12d4d0219b9cf113..6be8069e2d678cfd382d1cea12daee0a5788f64d 100644 (file)
@@ -857,7 +857,7 @@ dl_loader_t *dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool de
         *      emscripten, so keep this as a potentially
         *      runtime toggle for now.
         */
-#ifdef __EMCRIPTEN__
+#ifdef __EMSCRIPTEN__
        dl_loader->do_static = true;
 #else
        dl_loader->do_static = false;