1*8f1d5724Srobert //===----------------------------------------------------------------------===//
279c2e3e6Spatrick //
379c2e3e6Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
479c2e3e6Spatrick // See https://llvm.org/LICENSE.txt for license information.
579c2e3e6Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
679c2e3e6Spatrick //
779c2e3e6Spatrick //===----------------------------------------------------------------------===//
879c2e3e6Spatrick 
979c2e3e6Spatrick #include "abort_message.h"
1079c2e3e6Spatrick #include "cxxabi.h"
1179c2e3e6Spatrick #include <__threading_support>
1279c2e3e6Spatrick #ifndef _LIBCXXABI_HAS_NO_THREADS
1379c2e3e6Spatrick #if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB)
1479c2e3e6Spatrick #pragma comment(lib, "pthread")
1579c2e3e6Spatrick #endif
1679c2e3e6Spatrick #endif
1779c2e3e6Spatrick 
1879c2e3e6Spatrick #include <stdlib.h>
1979c2e3e6Spatrick 
2079c2e3e6Spatrick namespace __cxxabiv1 {
2179c2e3e6Spatrick 
2279c2e3e6Spatrick   using Dtor = void(*)(void*);
2379c2e3e6Spatrick 
2479c2e3e6Spatrick   extern "C"
2579c2e3e6Spatrick #ifndef HAVE___CXA_THREAD_ATEXIT_IMPL
2679c2e3e6Spatrick   // A weak symbol is used to detect this function's presence in the C library
2779c2e3e6Spatrick   // at runtime, even if libc++ is built against an older libc
2879c2e3e6Spatrick   _LIBCXXABI_WEAK
2979c2e3e6Spatrick #endif
3079c2e3e6Spatrick   int __cxa_thread_atexit_impl(Dtor, void*, void*);
3179c2e3e6Spatrick 
3279c2e3e6Spatrick #ifndef HAVE___CXA_THREAD_ATEXIT_IMPL
3379c2e3e6Spatrick 
3479c2e3e6Spatrick namespace {
3579c2e3e6Spatrick   // This implementation is used if the C library does not provide
3679c2e3e6Spatrick   // __cxa_thread_atexit_impl() for us.  It has a number of limitations that are
3779c2e3e6Spatrick   // difficult to impossible to address without ..._impl():
3879c2e3e6Spatrick   //
3979c2e3e6Spatrick   // - dso_symbol is ignored.  This means that a shared library may be unloaded
4079c2e3e6Spatrick   //   (via dlclose()) before its thread_local destructors have run.
4179c2e3e6Spatrick   //
4279c2e3e6Spatrick   // - thread_local destructors for the main thread are run by the destructor of
4379c2e3e6Spatrick   //   a static object.  This is later than expected; they should run before the
4479c2e3e6Spatrick   //   destructors of any objects with static storage duration.
4579c2e3e6Spatrick   //
4679c2e3e6Spatrick   // - thread_local destructors on non-main threads run on the first iteration
4779c2e3e6Spatrick   //   through the __libccpp_tls_key destructors.
4879c2e3e6Spatrick   //   std::notify_all_at_thread_exit() and similar functions must be careful to
4979c2e3e6Spatrick   //   wait until the second iteration to provide their intended ordering
5079c2e3e6Spatrick   //   guarantees.
5179c2e3e6Spatrick   //
5279c2e3e6Spatrick   // Another limitation, though one shared with ..._impl(), is that any
5379c2e3e6Spatrick   // thread_locals that are first initialized after non-thread_local global
5479c2e3e6Spatrick   // destructors begin to run will not be destroyed.  [basic.start.term] states
5579c2e3e6Spatrick   // that all thread_local destructors are sequenced before the destruction of
5679c2e3e6Spatrick   // objects with static storage duration, resulting in a contradiction if a
5779c2e3e6Spatrick   // thread_local is constructed after that point.  Thus we consider such
5879c2e3e6Spatrick   // programs ill-formed, and don't bother to run those destructors.  (If the
5979c2e3e6Spatrick   // program terminates abnormally after such a thread_local is constructed,
6079c2e3e6Spatrick   // the destructor is not expected to run and thus there is no contradiction.
6179c2e3e6Spatrick   // So construction still has to work.)
6279c2e3e6Spatrick 
6379c2e3e6Spatrick   struct DtorList {
6479c2e3e6Spatrick     Dtor dtor;
6579c2e3e6Spatrick     void* obj;
6679c2e3e6Spatrick     DtorList* next;
6779c2e3e6Spatrick   };
6879c2e3e6Spatrick 
6979c2e3e6Spatrick   // The linked list of thread-local destructors to run
7079c2e3e6Spatrick   __thread DtorList* dtors = nullptr;
7179c2e3e6Spatrick   // True if the destructors are currently scheduled to run on this thread
7279c2e3e6Spatrick   __thread bool dtors_alive = false;
7379c2e3e6Spatrick   // Used to trigger destructors on thread exit; value is ignored
7479c2e3e6Spatrick   std::__libcpp_tls_key dtors_key;
7579c2e3e6Spatrick 
run_dtors(void *)7679c2e3e6Spatrick   void run_dtors(void*) {
7779c2e3e6Spatrick     while (auto head = dtors) {
7879c2e3e6Spatrick       dtors = head->next;
7979c2e3e6Spatrick       head->dtor(head->obj);
8079c2e3e6Spatrick       ::free(head);
8179c2e3e6Spatrick     }
8279c2e3e6Spatrick 
8379c2e3e6Spatrick     dtors_alive = false;
8479c2e3e6Spatrick   }
8579c2e3e6Spatrick 
8679c2e3e6Spatrick   struct DtorsManager {
DtorsManager__cxxabiv1::__anoneeb4eb0a0111::DtorsManager8779c2e3e6Spatrick     DtorsManager() {
8879c2e3e6Spatrick       // There is intentionally no matching std::__libcpp_tls_delete call, as
8979c2e3e6Spatrick       // __cxa_thread_atexit() may be called arbitrarily late (for example, from
9079c2e3e6Spatrick       // global destructors or atexit() handlers).
9179c2e3e6Spatrick       if (std::__libcpp_tls_create(&dtors_key, run_dtors) != 0) {
9279c2e3e6Spatrick         abort_message("std::__libcpp_tls_create() failed in __cxa_thread_atexit()");
9379c2e3e6Spatrick       }
9479c2e3e6Spatrick     }
9579c2e3e6Spatrick 
~DtorsManager__cxxabiv1::__anoneeb4eb0a0111::DtorsManager9679c2e3e6Spatrick     ~DtorsManager() {
9779c2e3e6Spatrick       // std::__libcpp_tls_key destructors do not run on threads that call exit()
9879c2e3e6Spatrick       // (including when the main thread returns from main()), so we explicitly
9979c2e3e6Spatrick       // call the destructor here.  This runs at exit time (potentially earlier
10079c2e3e6Spatrick       // if libc++abi is dlclose()'d).  Any thread_locals initialized after this
10179c2e3e6Spatrick       // point will not be destroyed.
10279c2e3e6Spatrick       run_dtors(nullptr);
10379c2e3e6Spatrick     }
10479c2e3e6Spatrick   };
10579c2e3e6Spatrick } // namespace
10679c2e3e6Spatrick 
10779c2e3e6Spatrick #endif // HAVE___CXA_THREAD_ATEXIT_IMPL
10879c2e3e6Spatrick 
10979c2e3e6Spatrick extern "C" {
11079c2e3e6Spatrick 
__cxa_thread_atexit(Dtor dtor,void * obj,void * dso_symbol)11179c2e3e6Spatrick   _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() {
11279c2e3e6Spatrick #ifdef HAVE___CXA_THREAD_ATEXIT_IMPL
11379c2e3e6Spatrick     return __cxa_thread_atexit_impl(dtor, obj, dso_symbol);
11479c2e3e6Spatrick #else
11579c2e3e6Spatrick     if (__cxa_thread_atexit_impl) {
11679c2e3e6Spatrick       return __cxa_thread_atexit_impl(dtor, obj, dso_symbol);
11779c2e3e6Spatrick     } else {
11879c2e3e6Spatrick       // Initialize the dtors std::__libcpp_tls_key (uses __cxa_guard_*() for
11979c2e3e6Spatrick       // one-time initialization and __cxa_atexit() for destruction)
12079c2e3e6Spatrick       static DtorsManager manager;
12179c2e3e6Spatrick 
12279c2e3e6Spatrick       if (!dtors_alive) {
12379c2e3e6Spatrick         if (std::__libcpp_tls_set(dtors_key, &dtors_key) != 0) {
12479c2e3e6Spatrick           return -1;
12579c2e3e6Spatrick         }
12679c2e3e6Spatrick         dtors_alive = true;
12779c2e3e6Spatrick       }
12879c2e3e6Spatrick 
12979c2e3e6Spatrick       auto head = static_cast<DtorList*>(::malloc(sizeof(DtorList)));
13079c2e3e6Spatrick       if (!head) {
13179c2e3e6Spatrick         return -1;
13279c2e3e6Spatrick       }
13379c2e3e6Spatrick 
13479c2e3e6Spatrick       head->dtor = dtor;
13579c2e3e6Spatrick       head->obj = obj;
13679c2e3e6Spatrick       head->next = dtors;
13779c2e3e6Spatrick       dtors = head;
13879c2e3e6Spatrick 
13979c2e3e6Spatrick       return 0;
14079c2e3e6Spatrick     }
14179c2e3e6Spatrick #endif // HAVE___CXA_THREAD_ATEXIT_IMPL
14279c2e3e6Spatrick   }
14379c2e3e6Spatrick 
14479c2e3e6Spatrick } // extern "C"
14579c2e3e6Spatrick } // namespace __cxxabiv1
146