tavianator updated this revision to Diff 62691.
tavianator added a comment.

Added a test case for destructor ordering.  Got rid of 
pthread_{get,set}specific in a loop.


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
  test/thread_local_destruction_order.pass.cpp

Index: test/thread_local_destruction_order.pass.cpp
===================================================================
--- test/thread_local_destruction_order.pass.cpp
+++ test/thread_local_destruction_order.pass.cpp
@@ -0,0 +1,59 @@
+//===--------------------- cxa_thread_atexit_test.cpp ---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+#include <cassert>
+#include <thread>
+
+int seq = 0;
+
+class OrderChecker
+{
+public:
+  explicit OrderChecker(int n) : n_{n} { }
+
+  ~OrderChecker() {
+    assert(seq++ == n_);
+  }
+
+private:
+  int n_;
+};
+
+class CreatesThreadLocalInDestructor
+{
+public:
+  CreatesThreadLocalInDestructor(int n) : n_{n} { }
+
+  ~CreatesThreadLocalInDestructor() {
+    thread_local OrderChecker checker{n_};
+  }
+
+private:
+  int n_;
+};
+
+OrderChecker global{5};
+
+void thread_fn() {
+  static OrderChecker fn_static{3};
+  thread_local OrderChecker fn_thread_local{0};
+}
+
+int main() {
+  static OrderChecker fn_static{4};
+
+  std::thread{thread_fn}.join();
+
+  thread_local OrderChecker fn_thread_local{2};
+  thread_local CreatesThreadLocalInDestructor creates_tl{1};
+
+  return 0;
+}
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,126 @@
 //
 //===----------------------------------------------------------------------===//
 
+#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 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 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.
+
+  using Dtor = void(*)(void*);
+
+  struct DtorList {
+    Dtor dtor;
+    void* obj;
+    DtorList* next;
+  };
+
+  pthread_key_t dtors;
+
+  void run_dtors(void* ptr) {
+    // pthread_key destructors run after the key is set to NULL, but we need to
+    // restore it here to get the right ordering if __cxa_thread_atexit() is
+    // called during the loop.
+    if (pthread_setspecific(dtors, ptr) != 0) {
+      abort_message("pthread_setspecific() failed during thread_local destruction");
+    }
+
+    auto list = static_cast<DtorList**>(ptr);
+
+    while (auto head = *list) {
+      *list = head->next;
+      head->dtor(head->obj);
+      std::free(head);
+    }
+
+    std::free(list);
+
+    // Set the pthread_key to NULL (or else the destructor would run again)
+    if (pthread_setspecific(dtors, NULL) != 0) {
+      abort_message("pthread_setspecific() failed during thread_local destruction");
+    }
+  }
+
+  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, 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).
+      auto ptr = pthread_getspecific(dtors);
+      if (ptr) {
+        run_dtors(ptr);
+      }
+    }
+  };
+} // 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 {
+    // Initialize the dtors pthread_key (uses __cxa_guard_*() for one-time
+    // initialization and __cxa_atexit() for destruction)
+    static DtorsManager manager;
+
+    auto list = static_cast<DtorList**>(pthread_getspecific(dtors));
+    if (!list) {
+      list = static_cast<DtorList**>(std::malloc(sizeof(DtorList*)));
+      if (!list) {
+        return -1;
+      }
+      if (pthread_setspecific(dtors, list) != 0) {
+        return -1;
+      }
+    }
+
+    auto head = static_cast<DtorList*>(std::malloc(sizeof(DtorList)));
+    if (!head) {
+      return -1;
+    }
+
+    head->dtor = dtor;
+    head->obj = obj;
+    head->next = *list;
+    *list = head;
 
-_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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to