Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -43,5 +43,3 @@ check_library_exists(dl dladdr "" LIBCXXABI_HAS_DL_LIB) check_library_exists(pthread pthread_once "" LIBCXXABI_HAS_PTHREAD_LIB) check_library_exists(gcc_s __gcc_personality_v0 "" LIBCXXABI_HAS_GCC_S_LIB) -check_library_exists(c __cxa_thread_atexit_impl "" - LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) Index: src/CMakeLists.txt =================================================================== --- src/CMakeLists.txt +++ src/CMakeLists.txt @@ -41,10 +41,6 @@ include_directories("${LIBCXXABI_LIBCXX_INCLUDES}") -if (LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) - add_definitions(-DHAVE___CXA_THREAD_ATEXIT_IMPL) -endif() - # Generate library list set(libraries ${LIBCXXABI_CXX_ABI_LIBRARIES}) Index: src/cxa_thread_atexit.cpp =================================================================== --- src/cxa_thread_atexit.cpp +++ src/cxa_thread_atexit.cpp @@ -7,20 +7,137 @@ // //===----------------------------------------------------------------------===// +#include "abort_message.h" #include "cxxabi.h" +#include +#include namespace __cxxabiv1 { +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 with __cxa_atexit(). + // This is later than expected; they should run before the destructors of + // any objects with static storage duration. + // + // - thread_local destructors on other 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 indended ordering guarantees. + + typedef void (*Dtor)(void*); + + struct DtorList { + Dtor dtor; + void* obj; + DtorList* next; + }; + + pthread_key_t dtors; + pthread_once_t dtors_once = PTHREAD_ONCE_INIT; + bool dtors_ready = false; + bool dtors_atexit = false; + + void run_dtors(void* ptr) { + if (pthread_setspecific(dtors, ptr) != 0) { + abort_message("pthread_setspecific() failed during thread_local destruction"); + } + + // Rather than iterate over the list directly, use the pthread_key to get + // the head of the list every time. This gives the correct ordering if + // __cxa_thread_atexit() is called during the loop. + while (auto elem = static_cast(pthread_getspecific(dtors))) { + if (pthread_setspecific(dtors, elem->next) != 0) { + abort_message("pthread_setspecific() failed during thread_local destruction"); + } + elem->dtor(elem->obj); + std::free(elem); + } + } + + void run_dtors_atexit(void*) { + // Signify that we need to re-register this function if any new + // thread_locals are created. + dtors_atexit = false; + + auto ptr = pthread_getspecific(dtors); + run_dtors(ptr); + } + + // This is the DSO handle for libc++abi.so itself + extern "C" void* __dso_handle; + + void dtors_init() { + // 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, run_dtors) != 0) { + return; + } + + // pthread_key destructors do not run on threads that call exit() (including + // when the main thread returns from main()). Explicitly register an atexit + // handler to handle that case. + if (__cxa_atexit(run_dtors_atexit, NULL, __dso_handle) != 0) { + return; + } + + dtors_ready = true; + dtors_atexit = true; + } +} // namespace + extern "C" { -#ifdef HAVE___CXA_THREAD_ATEXIT_IMPL +_LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, + void* dso_symbol) throw() { + // A weak symbol is used to detect this function's presence in the C library + extern int __cxa_thread_atexit_impl(Dtor, void *, void *) + __attribute__((__weak__)); + + if (__cxa_thread_atexit_impl) { + return __cxa_thread_atexit_impl(dtor, obj, dso_symbol); + } else { + if (pthread_once(&dtors_once, dtors_init) != 0) { + return -1; + } + if (!dtors_ready) { + return -1; + } + + // It's possible for __cxa_thread_atexit() to be called after dtors_atexit() + // has already run, for example if a global destructor or atexit() handler + // reaches a thread_local for the first time. Re-register the atexit() + // handler in that case. + if (!dtors_atexit) { + if (__cxa_atexit(run_dtors_atexit, NULL, __dso_handle) != 0) { + return -1; + } + dtors_atexit = true; + } + + auto head = static_cast(std::malloc(sizeof(DtorList))); + if (!head) { + return -1; + } + + head->dtor = dtor; + head->obj = obj; + head->next = static_cast(pthread_getspecific(dtors)); + + if (pthread_setspecific(dtors, head) != 0) { + std::free(head); + return -1; + } -_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); + return 0; + } } -#endif // HAVE__CXA_THREAD_ATEXIT_IMPL - } // extern "C" } // namespace __cxxabiv1 Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -16,7 +16,6 @@ pythonize_bool(LIBCXXABI_ENABLE_THREADS) pythonize_bool(LIBCXXABI_ENABLE_EXCEPTIONS) pythonize_bool(LIBCXXABI_USE_LLVM_UNWINDER) -pythonize_bool(LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL) set(LIBCXXABI_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING "TargetInfo to use when setting up test environment.") set(LIBCXXABI_EXECUTOR "None" CACHE STRING Index: test/cxa_thread_atexit_test.pass.cpp =================================================================== --- test/cxa_thread_atexit_test.pass.cpp +++ test/cxa_thread_atexit_test.pass.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// // REQUIRES: linux -// REQUIRES: thread_atexit #include #include Index: test/libcxxabi/test/config.py =================================================================== --- test/libcxxabi/test/config.py +++ test/libcxxabi/test/config.py @@ -37,8 +37,6 @@ super(Configuration, self).configure_features() if not self.get_lit_bool('enable_exceptions', True): self.config.available_features.add('libcxxabi-no-exceptions') - if self.get_lit_bool('thread_atexit', True): - self.config.available_features.add('thread_atexit') def configure_compile_flags(self): self.cxx.compile_flags += ['-DLIBCXXABI_NO_TIMER'] Index: test/lit.site.cfg.in =================================================================== --- test/lit.site.cfg.in +++ test/lit.site.cfg.in @@ -13,7 +13,6 @@ config.enable_32bit = "@LIBCXXABI_BUILD_32_BITS@" config.target_info = "@LIBCXXABI_TARGET_INFO@" config.executor = "@LIBCXXABI_EXECUTOR@" -config.thread_atexit = "@LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL@" config.libcxxabi_shared = "@LIBCXXABI_ENABLE_SHARED@" config.enable_shared = "@LIBCXX_ENABLE_SHARED@" config.enable_exceptions = "@LIBCXXABI_ENABLE_EXCEPTIONS@"