tavianator updated this revision to Diff 62386.
tavianator added a comment.
Fixed some corner cases regarding destruction order and very-late-initialized
thread_locals. Explicitly documented the known limitations compared to
__cxa_thread_atexit_impl().
http://reviews.llvm.org/D21803
Files:
cmake/config-ix.cmake
src/CMakeLists.txt
src/cxa_thread_atexit.cpp
test/CMakeLists.txt
test/cxa_thread_atexit_test.pass.cpp
test/libcxxabi/test/config.py
test/lit.site.cfg.in
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@"
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/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 <assert.h>
#include <cxxabi.h>
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: src/cxa_thread_atexit.cpp
===================================================================
--- src/cxa_thread_atexit.cpp
+++ src/cxa_thread_atexit.cpp
@@ -7,20 +7,134 @@
//
//===----------------------------------------------------------------------===//
+#include "abort_message.h"
#include "cxxabi.h"
+#include <cstdlib>
+#include <pthread.h>
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<DtorList*>(pthread_getspecific(dtors))) {
+ if (pthread_setspecific(dtors, ptr) != 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);
+ }
+
+ 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<DtorList*>(std::malloc(sizeof(DtorList)));
+ if (!head) {
+ return -1;
+ }
+
+ head->dtor = dtor;
+ head->obj = obj;
+ head->next = static_cast<DtorList*>(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: 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: 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)
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits