diff options
author | Eric Fiselier <eric@efcs.ca> | 2016-10-12 08:54:10 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2016-10-12 08:54:10 +0000 |
commit | 3f7b3700184b818c28ee085e3d629aa5f314169b (patch) | |
tree | adc49ec6b1a4cfb30d256e0d96baa85d1bf9ccef /src/cxa_thread_atexit.cpp | |
parent | 5ab59762e2b8bae03dfd61c41906c43cf8c7366e (diff) |
Provide a fallback __cxa_thread_atexit() implementation. Patch from Tavian Barnes
git-svn-id: https://llvm.org/svn/llvm-project/libcxxabi/trunk@283988 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'src/cxa_thread_atexit.cpp')
-rw-r--r-- | src/cxa_thread_atexit.cpp | 125 |
1 files changed, 119 insertions, 6 deletions
diff --git a/src/cxa_thread_atexit.cpp b/src/cxa_thread_atexit.cpp index 7962e28..dea5c21 100644 --- a/src/cxa_thread_atexit.cpp +++ b/src/cxa_thread_atexit.cpp @@ -7,20 +7,133 @@ // //===----------------------------------------------------------------------===// +#include "abort_message.h" #include "cxxabi.h" +#include <cstdlib> +#include <pthread.h> namespace __cxxabiv1 { + + using Dtor = void(*)(void*); + + extern "C" +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + // A weak symbol is used to detect this function's presence in the C library + // at runtime, even if libc++ is built against an older libc + __attribute__((__weak__)) +#endif + int __cxa_thread_atexit_impl(Dtor, void*, void*); + +#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL + +namespace { + // This implementation is used if the C library does not provide + // __cxa_thread_atexit_impl() for us. It has a number of limitations that are + // difficult to impossible to address without ..._impl(): + // + // - dso_symbol is ignored. This means that a shared library may be unloaded + // (via dlclose()) before its thread_local destructors have run. + // + // - thread_local destructors for the main thread are run by the destructor of + // a static object. This is later than expected; they should run before the + // destructors of any objects with static storage duration. + // + // - thread_local destructors on non-main threads run on the first iteration + // through the pthread_key destructors. std::notify_all_at_thread_exit() + // and similar functions must be careful to wait until the second iteration + // to provide their intended ordering guarantees. + // + // Another limitation, though one shared with ..._impl(), is that any + // thread_locals that are first initialized after non-thread_local global + // destructors begin to run will not be destroyed. [basic.start.term] states + // that all thread_local destructors are sequenced before the destruction of + // objects with static storage duration, resulting in a contradiction if a + // thread_local is constructed after that point. Thus we consider such + // programs ill-formed, and don't bother to run those destructors. (If the + // program terminates abnormally after such a thread_local is constructed, + // the destructor is not expected to run and thus there is no contradiction. + // So construction still has to work.) + + struct DtorList { + Dtor dtor; + void* obj; + DtorList* next; + }; + + // The linked list of thread-local destructors to run + __thread DtorList* dtors = nullptr; + // True if the destructors are currently scheduled to run on this thread + __thread bool dtors_alive = false; + // Used to trigger destructors on thread exit; value is ignored + pthread_key_t dtors_key; + + void run_dtors(void*) { + while (auto head = dtors) { + dtors = head->next; + head->dtor(head->obj); + std::free(head); + } + + dtors_alive = false; + } + + struct DtorsManager { + DtorsManager() { + // There is intentionally no matching pthread_key_delete call, as + // __cxa_thread_atexit() may be called arbitrarily late (for example, from + // global destructors or atexit() handlers). + if (pthread_key_create(&dtors_key, run_dtors) != 0) { + abort_message("pthread_key_create() failed in __cxa_thread_atexit()"); + } + } + + ~DtorsManager() { + // pthread_key destructors do not run on threads that call exit() + // (including when the main thread returns from main()), so we explicitly + // call the destructor here. This runs at exit time (potentially earlier + // if libc++abi is dlclose()'d). Any thread_locals initialized after this + // point will not be destroyed. + run_dtors(nullptr); + } + }; +} // namespace + +#endif // HAVE__CXA_THREAD_ATEXIT_IMPL + extern "C" { + _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() { #ifdef HAVE___CXA_THREAD_ATEXIT_IMPL + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); +#else + if (__cxa_thread_atexit_impl) { + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); + } else { + // Initialize the dtors pthread_key (uses __cxa_guard_*() for one-time + // initialization and __cxa_atexit() for destruction) + static DtorsManager manager; -_LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(void (*dtor)(void *), void *obj, - void *dso_symbol) throw() { - extern int __cxa_thread_atexit_impl(void (*)(void *), void *, void *); - return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); -} + if (!dtors_alive) { + if (pthread_setspecific(dtors_key, &dtors_key) != 0) { + return -1; + } + dtors_alive = true; + } -#endif // HAVE__CXA_THREAD_ATEXIT_IMPL + auto head = static_cast<DtorList*>(std::malloc(sizeof(DtorList))); + if (!head) { + return -1; + } + + head->dtor = dtor; + head->obj = obj; + head->next = dtors; + dtors = head; + + return 0; + } +#endif // HAVE___CXA_THREAD_ATEXIT_IMPL + } } // extern "C" } // namespace __cxxabiv1 |