Ticket #52585: cxa_thread_atexit-TLS.diff

File cxa_thread_atexit-TLS.diff, 4.9 KB (added by ken-cunningham-webuse, 8 years ago)

diff from existing @3.7.0 src/cxa_thread_atexit.cpp

  • src/cxa_thread_atexit.cpp

    old new  
    77//
    88//===----------------------------------------------------------------------===//
    99
     10#include "abort_message.h"
    1011#include "cxxabi.h"
     12#include <cstdlib>
     13#include <pthread.h>
    1114
    1215namespace __cxxabiv1 {
    1316
    14 extern "C" {
     17using Dtor = void (*)(void *);
    1518
    16 #ifdef HAVE___CXA_THREAD_ATEXIT_IMPL
     19extern "C"
    1720
    18 int __cxa_thread_atexit(void (*dtor)(void *), void *obj,
    19                         void *dso_symbol) throw() {
    20   extern int __cxa_thread_atexit_impl(void (*)(void *), void *, void *);
    21   return __cxa_thread_atexit_impl(dtor, obj, dso_symbol);
     21#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL
     22    // A weak symbol is used to detect this function's presence in the C library
     23    // at runtime, even if libc++ is built against an older libc
     24    __attribute__((__weak__))
     25#endif
     26
     27int __cxa_thread_atexit_impl(Dtor, void *, void *);
     28
     29#ifndef HAVE___CXA_THREAD_ATEXIT_IMPL
     30
     31namespace {
     32// This implementation is used if the C library does not provide
     33// __cxa_thread_atexit_impl() for us.  It has a number of limitations that are
     34// difficult to impossible to address without ..._impl():
     35//
     36// - dso_symbol is ignored.  This means that a shared library may be unloaded
     37//   (via dlclose()) before its thread_local destructors have run.
     38//
     39// - thread_local destructors for the main thread are run by the destructor of
     40//   a static object.  This is later than expected; they should run before the
     41//   destructors of any objects with static storage duration.
     42//
     43// - thread_local destructors on non-main threads run on the first iteration
     44//   through the pthread_key destructors.  std::notify_all_at_thread_exit()
     45//   and similar functions must be careful to wait until the second iteration
     46//   to provide their intended ordering guarantees.
     47//
     48// Another limitation, though one shared with ..._impl(), is that any
     49// thread_locals that are first initialized after non-thread_local global
     50// destructors begin to run will not be destroyed.  [basic.start.term] states
     51// that all thread_local destructors are sequenced before the destruction of
     52// objects with static storage duration, resulting in a contradiction if a
     53// thread_local is constructed after that point.  Thus we consider such
     54// programs ill-formed, and don't bother to run those destructors.  (If the
     55// program terminates abnormally after such a thread_local is constructed,
     56// the destructor is not expected to run and thus there is no contradiction.
     57// So construction still has to work.)
     58
     59struct DtorList {
     60  Dtor dtor;
     61  void *obj;
     62  DtorList *next;
     63};
     64
     65// The linked list of thread-local destructors to run
     66__thread DtorList *dtors = nullptr;
     67// True if the destructors are currently scheduled to run on this thread
     68__thread bool dtors_alive = false;
     69// Used to trigger destructors on thread exit; value is ignored
     70pthread_key_t dtors_key;
     71
     72void run_dtors(void *) {
     73  while (auto head = dtors) {
     74    dtors = head->next;
     75    head->dtor(head->obj);
     76    std::free(head);
     77  }
     78
     79  dtors_alive = false;
    2280}
    2381
     82struct DtorsManager {
     83  DtorsManager() {
     84    // There is intentionally no matching pthread_key_delete call, as
     85    // __cxa_thread_atexit() may be called arbitrarily late (for example, from
     86    // global destructors or atexit() handlers).
     87    if (pthread_key_create(&dtors_key, run_dtors) != 0) {
     88      abort_message("pthread_key_create() failed in __cxa_thread_atexit()");
     89    }
     90  }
     91
     92  ~DtorsManager() {
     93    // pthread_key destructors do not run on threads that call exit()
     94    // (including when the main thread returns from main()), so we explicitly
     95    // call the destructor here.  This runs at exit time (potentially earlier
     96    // if libc++abi is dlclose()'d).  Any thread_locals initialized after this
     97    // point will not be destroyed.
     98    run_dtors(nullptr);
     99  }
     100};
     101} // namespace
     102
    24103#endif // HAVE__CXA_THREAD_ATEXIT_IMPL
    25104
     105extern "C" {
     106
     107int __cxa_thread_atexit(Dtor dtor, void *obj, void *dso_symbol) throw() {
     108
     109#ifdef HAVE___CXA_THREAD_ATEXIT_IMPL
     110
     111  return __cxa_thread_atexit_impl(dtor, obj, dso_symbol);
     112#else
     113  if (__cxa_thread_atexit_impl) {
     114    return __cxa_thread_atexit_impl(dtor, obj, dso_symbol);
     115  } else {
     116    // Initialize the dtors pthread_key (uses __cxa_guard_*() for one-time
     117    // initialization and __cxa_atexit() for destruction)
     118    static DtorsManager manager;
     119
     120    if (!dtors_alive) {
     121      if (pthread_setspecific(dtors_key, &dtors_key) != 0) {
     122        return -1;
     123      }
     124      dtors_alive = true;
     125    }
     126
     127    auto head = static_cast<DtorList *>(std::malloc(sizeof(DtorList)));
     128    if (!head) {
     129      return -1;
     130    }
     131
     132    head->dtor = dtor;
     133    head->obj = obj;
     134    head->next = dtors;
     135    dtors = head;
     136
     137    return 0;
     138  }
     139#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
     140}
    26141} // extern "C"
    27142
    28143} // namespace __cxxabiv1