1 | //===----------------------- cxa_thread_atexit.cpp ------------------------===// |
---|
2 | // |
---|
3 | // The LLVM Compiler Infrastructure |
---|
4 | // |
---|
5 | // This file is dual licensed under the MIT and the University of Illinois Open |
---|
6 | // Source Licenses. See LICENSE.TXT for details. |
---|
7 | // |
---|
8 | //===----------------------------------------------------------------------===// |
---|
9 | |
---|
10 | #include "abort_message.h" |
---|
11 | #include "cxxabi.h" |
---|
12 | #include <cstdlib> |
---|
13 | #include <pthread.h> |
---|
14 | |
---|
15 | namespace __cxxabiv1 { |
---|
16 | |
---|
17 | using Dtor = void (*)(void *); |
---|
18 | |
---|
19 | extern "C" |
---|
20 | |
---|
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 | |
---|
27 | int __cxa_thread_atexit_impl(Dtor, void *, void *); |
---|
28 | |
---|
29 | #ifndef HAVE___CXA_THREAD_ATEXIT_IMPL |
---|
30 | |
---|
31 | namespace { |
---|
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 | |
---|
59 | struct 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 |
---|
70 | pthread_key_t dtors_key; |
---|
71 | |
---|
72 | void 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; |
---|
80 | } |
---|
81 | |
---|
82 | struct 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 | |
---|
103 | #endif // HAVE__CXA_THREAD_ATEXIT_IMPL |
---|
104 | |
---|
105 | extern "C" { |
---|
106 | |
---|
107 | int __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 | } |
---|
141 | } // extern "C" |
---|
142 | |
---|
143 | } // namespace __cxxabiv1 |
---|