1//===----------------------- cxa_thread_atexit.cpp ------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9/** This file was edited for ClickHouse.
10 * This is needed to avoid linking with "__cxa_thread_atexit_impl" function, that require too new (2.18) glibc library.
11 *
12 * Note: "__cxa_thread_atexit_impl" may provide sophisticated implementation to correct destruction of thread-local objects,
13 * that was created in different DSO. Read https://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables
14 * We simply don't need this implementation, because we don't use thread-local objects from different DSO.
15 */
16
17#include "abort_message.h"
18#include "cxxabi.h"
19#include <__threading_support>
20#ifndef _LIBCXXABI_HAS_NO_THREADS
21#if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB)
22#pragma comment(lib, "pthread")
23#endif
24#endif
25
26#include <stdlib.h>
27
28namespace __cxxabiv1 {
29
30 using Dtor = void(*)(void*);
31
32
33namespace {
34 // This implementation is used if the C library does not provide
35 // __cxa_thread_atexit_impl() for us. It has a number of limitations that are
36 // difficult to impossible to address without ..._impl():
37 //
38 // - dso_symbol is ignored. This means that a shared library may be unloaded
39 // (via dlclose()) before its thread_local destructors have run.
40 //
41 // - thread_local destructors for the main thread are run by the destructor of
42 // a static object. This is later than expected; they should run before the
43 // destructors of any objects with static storage duration.
44 //
45 // - thread_local destructors on non-main threads run on the first iteration
46 // through the __libccpp_tls_key destructors.
47 // std::notify_all_at_thread_exit() and similar functions must be careful to
48 // wait until the second iteration to provide their intended ordering
49 // guarantees.
50 //
51 // Another limitation, though one shared with ..._impl(), is that any
52 // thread_locals that are first initialized after non-thread_local global
53 // destructors begin to run will not be destroyed. [basic.start.term] states
54 // that all thread_local destructors are sequenced before the destruction of
55 // objects with static storage duration, resulting in a contradiction if a
56 // thread_local is constructed after that point. Thus we consider such
57 // programs ill-formed, and don't bother to run those destructors. (If the
58 // program terminates abnormally after such a thread_local is constructed,
59 // the destructor is not expected to run and thus there is no contradiction.
60 // So construction still has to work.)
61
62 struct DtorList {
63 Dtor dtor;
64 void* obj;
65 DtorList* next;
66 };
67
68 // The linked list of thread-local destructors to run
69 __thread DtorList* dtors = nullptr;
70 // True if the destructors are currently scheduled to run on this thread
71 __thread bool dtors_alive = false;
72 // Used to trigger destructors on thread exit; value is ignored
73 std::__libcpp_tls_key dtors_key;
74
75 void run_dtors(void*) {
76 while (auto head = dtors) {
77 dtors = head->next;
78 head->dtor(head->obj);
79 ::free(head);
80 }
81
82 dtors_alive = false;
83 }
84
85 struct DtorsManager {
86 DtorsManager() {
87 // There is intentionally no matching std::__libcpp_tls_delete call, as
88 // __cxa_thread_atexit() may be called arbitrarily late (for example, from
89 // global destructors or atexit() handlers).
90 if (std::__libcpp_tls_create(&dtors_key, run_dtors) != 0) {
91 abort_message("std::__libcpp_tls_create() failed in __cxa_thread_atexit()");
92 }
93 }
94
95 ~DtorsManager() {
96 // std::__libcpp_tls_key destructors do not run on threads that call exit()
97 // (including when the main thread returns from main()), so we explicitly
98 // call the destructor here. This runs at exit time (potentially earlier
99 // if libc++abi is dlclose()'d). Any thread_locals initialized after this
100 // point will not be destroyed.
101 run_dtors(nullptr);
102 }
103 };
104} // namespace
105
106
107extern "C" {
108
109 _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit_impl(Dtor dtor, void* obj, void* dso_symbol) throw() {
110 // Initialize the dtors std::__libcpp_tls_key (uses __cxa_guard_*() for
111 // one-time initialization and __cxa_atexit() for destruction)
112 static DtorsManager manager;
113
114 if (!dtors_alive) {
115 if (std::__libcpp_tls_set(dtors_key, &dtors_key) != 0) {
116 return -1;
117 }
118 dtors_alive = true;
119 }
120
121 auto head = static_cast<DtorList*>(::malloc(sizeof(DtorList)));
122 if (!head) {
123 return -1;
124 }
125
126 head->dtor = dtor;
127 head->obj = obj;
128 head->next = dtors;
129 dtors = head;
130
131 return 0;
132 }
133
134 int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw()
135 {
136 return __cxa_thread_atexit_impl(dtor, obj, dso_symbol);
137 }
138
139} // extern "C"
140} // namespace __cxxabiv1
141