aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/libsupc++/atexit_thread.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libstdc++-v3/libsupc++/atexit_thread.cc')
-rw-r--r--libstdc++-v3/libsupc++/atexit_thread.cc151
1 files changed, 67 insertions, 84 deletions
diff --git a/libstdc++-v3/libsupc++/atexit_thread.cc b/libstdc++-v3/libsupc++/atexit_thread.cc
index 5e47708d934..95bdcf09dec 100644
--- a/libstdc++-v3/libsupc++/atexit_thread.cc
+++ b/libstdc++-v3/libsupc++/atexit_thread.cc
@@ -27,109 +27,92 @@
#include "bits/gthr.h"
namespace {
- // Data structure for the list of destructors: Singly-linked list
- // of arrays.
- class list
+ // One element in a singly-linked stack of cleanups.
+ struct elt
{
- struct elt
- {
- void *object;
- void (*destructor)(void *);
- };
-
- static const int max_nelts = 32;
-
- list *next;
- int nelts;
- elt array[max_nelts];
-
- elt *allocate_elt();
- public:
- void run();
- static void run(void *p);
- int add_elt(void (*)(void *), void *);
+ void (*destructor)(void *);
+ void *object;
+ elt *next;
};
- // Return the address of an open slot.
- list::elt *
- list::allocate_elt()
- {
- if (nelts < max_nelts)
- return &array[nelts++];
- if (!next)
- next = new (std::nothrow) list();
- if (!next)
- return 0;
- return next->allocate_elt();
- }
+ // Keep a per-thread list of cleanups in gthread_key storage.
+ __gthread_key_t key;
+ // But also support non-threaded mode.
+ elt *single_thread;
- // Run all the cleanups in the list.
- void
- list::run()
+ // Run the specified stack of cleanups.
+ void run (void *p)
{
- for (int i = nelts - 1; i >= 0; --i)
- array[i].destructor (array[i].object);
- if (next)
- next->run();
+ elt *e = static_cast<elt*>(p);
+ for (; e; e = e->next)
+ e->destructor (e->object);
}
- // Static version to use as a callback to __gthread_key_create.
- void
- list::run(void *p)
+ // Run the stack of cleanups for the current thread.
+ void run ()
{
- static_cast<list *>(p)->run();
+ void *e;
+ if (__gthread_active_p ())
+ e = __gthread_getspecific (key);
+ else
+ e = single_thread;
+ run (e);
}
- // The list of cleanups is per-thread.
- thread_local list first;
-
- // The pthread data structures for actually running the destructors at
- // thread exit are shared. The constructor of the thread-local sentinel
- // object in add_elt performs the initialization.
- __gthread_key_t key;
- __gthread_once_t once = __GTHREAD_ONCE_INIT;
- void run_current () { first.run(); }
+ // Initialize the key for the cleanup stack. We use a static local for
+ // key init/delete rather than atexit so that delete is run on dlclose.
void key_init() {
- __gthread_key_create (&key, list::run);
+ struct key_s {
+ key_s() { __gthread_key_create (&key, run); }
+ ~key_s() { __gthread_key_delete (key); }
+ };
+ static key_s ks;
// Also make sure the destructors are run by std::exit.
// FIXME TLS cleanups should run before static cleanups and atexit
// cleanups.
- std::atexit (run_current);
+ std::atexit (run);
}
- struct sentinel
- {
- sentinel()
+}
+
+extern "C" int
+__cxxabiv1::__cxa_thread_atexit (void (*dtor)(void *), void *obj, void */*dso_handle*/)
+ _GLIBCXX_NOTHROW
+{
+ // Do this initialization once.
+ if (__gthread_active_p ())
+ {
+ // When threads are active use __gthread_once.
+ static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+ __gthread_once (&once, key_init);
+ }
+ else
{
- if (__gthread_active_p ())
+ // And when threads aren't active use a static local guard.
+ static bool queued;
+ if (!queued)
{
- __gthread_once (&once, key_init);
- __gthread_setspecific (key, &first);
+ queued = true;
+ std::atexit (run);
}
- else
- std::atexit (run_current);
}
- };
- // Actually insert an element.
- int
- list::add_elt(void (*dtor)(void *), void *obj)
- {
- thread_local sentinel s;
- elt *e = allocate_elt ();
- if (!e)
- return -1;
- e->object = obj;
- e->destructor = dtor;
- return 0;
- }
-}
+ elt *first;
+ if (__gthread_active_p ())
+ first = static_cast<elt*>(__gthread_getspecific (key));
+ else
+ first = single_thread;
-namespace __cxxabiv1
-{
- extern "C" int
- __cxa_thread_atexit (void (*dtor)(void *), void *obj, void */*dso_handle*/)
- _GLIBCXX_NOTHROW
- {
- return first.add_elt (dtor, obj);
- }
+ elt *new_elt = new (std::nothrow) elt;
+ if (!new_elt)
+ return -1;
+ new_elt->destructor = dtor;
+ new_elt->object = obj;
+ new_elt->next = first;
+
+ if (__gthread_active_p ())
+ __gthread_setspecific (key, new_elt);
+ else
+ single_thread = new_elt;
+
+ return 0;
}