1 | //===--- Definitions of common thread items ---------------------*- C++ -*-===// |
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 | #include "thread.h" |
10 | #include "mutex.h" |
11 | |
12 | #include "src/__support/CPP/array.h" |
13 | #include "src/__support/CPP/optional.h" |
14 | #include "src/__support/fixedvector.h" |
15 | #include "src/__support/macros/attributes.h" |
16 | |
17 | namespace LIBC_NAMESPACE { |
18 | |
19 | LIBC_THREAD_LOCAL Thread self; |
20 | |
21 | namespace { |
22 | |
23 | using AtExitCallback = void(void *); |
24 | |
25 | struct AtExitUnit { |
26 | AtExitCallback *callback = nullptr; |
27 | void *obj = nullptr; |
28 | constexpr AtExitUnit() = default; |
29 | constexpr AtExitUnit(AtExitCallback *cb, void *o) : callback(cb), obj(o) {} |
30 | }; |
31 | |
32 | constexpr size_t TSS_KEY_COUNT = 1024; |
33 | |
34 | struct TSSKeyUnit { |
35 | // Indicates whether is unit is active. Presence of a non-null dtor |
36 | // is not sufficient to indicate the same information as a TSS key can |
37 | // have a null destructor. |
38 | bool active = false; |
39 | |
40 | TSSDtor *dtor = nullptr; |
41 | |
42 | constexpr TSSKeyUnit() = default; |
43 | constexpr TSSKeyUnit(TSSDtor *d) : active(true), dtor(d) {} |
44 | |
45 | void reset() { |
46 | active = false; |
47 | dtor = nullptr; |
48 | } |
49 | }; |
50 | |
51 | class TSSKeyMgr { |
52 | Mutex mtx; |
53 | cpp::array<TSSKeyUnit, TSS_KEY_COUNT> units; |
54 | |
55 | public: |
56 | constexpr TSSKeyMgr() : mtx(false, false, false) {} |
57 | |
58 | cpp::optional<unsigned int> new_key(TSSDtor *dtor) { |
59 | MutexLock lock(&mtx); |
60 | for (unsigned int i = 0; i < TSS_KEY_COUNT; ++i) { |
61 | TSSKeyUnit &u = units[i]; |
62 | if (!u.active) { |
63 | u = {dtor}; |
64 | return i; |
65 | } |
66 | } |
67 | return cpp::optional<unsigned int>(); |
68 | } |
69 | |
70 | TSSDtor *get_dtor(unsigned int key) { |
71 | if (key >= TSS_KEY_COUNT) |
72 | return nullptr; |
73 | MutexLock lock(&mtx); |
74 | return units[key].dtor; |
75 | } |
76 | |
77 | bool remove_key(unsigned int key) { |
78 | if (key >= TSS_KEY_COUNT) |
79 | return false; |
80 | MutexLock lock(&mtx); |
81 | units[key].reset(); |
82 | return true; |
83 | } |
84 | |
85 | bool is_valid_key(unsigned int key) { |
86 | MutexLock lock(&mtx); |
87 | return units[key].active; |
88 | } |
89 | }; |
90 | |
91 | TSSKeyMgr tss_key_mgr; |
92 | |
93 | struct TSSValueUnit { |
94 | bool active = false; |
95 | void *payload = nullptr; |
96 | TSSDtor *dtor = nullptr; |
97 | |
98 | constexpr TSSValueUnit() = default; |
99 | constexpr TSSValueUnit(void *p, TSSDtor *d) |
100 | : active(true), payload(p), dtor(d) {} |
101 | }; |
102 | |
103 | static LIBC_THREAD_LOCAL cpp::array<TSSValueUnit, TSS_KEY_COUNT> tss_values; |
104 | |
105 | } // anonymous namespace |
106 | |
107 | class ThreadAtExitCallbackMgr { |
108 | Mutex mtx; |
109 | // TODO: Use a BlockStore when compiled for production. |
110 | FixedVector<AtExitUnit, 1024> callback_list; |
111 | |
112 | public: |
113 | constexpr ThreadAtExitCallbackMgr() : mtx(false, false, false) {} |
114 | |
115 | int add_callback(AtExitCallback *callback, void *obj) { |
116 | MutexLock lock(&mtx); |
117 | return callback_list.push_back(obj: {callback, obj}); |
118 | } |
119 | |
120 | void call() { |
121 | mtx.lock(); |
122 | while (!callback_list.empty()) { |
123 | auto atexit_unit = callback_list.back(); |
124 | callback_list.pop_back(); |
125 | mtx.unlock(); |
126 | atexit_unit.callback(atexit_unit.obj); |
127 | mtx.lock(); |
128 | } |
129 | } |
130 | }; |
131 | |
132 | static LIBC_THREAD_LOCAL ThreadAtExitCallbackMgr atexit_callback_mgr; |
133 | |
134 | // The function __cxa_thread_atexit is provided by C++ runtimes like libcxxabi. |
135 | // It is used by thread local object runtime to register destructor calls. To |
136 | // actually register destructor call with the threading library, it calls |
137 | // __cxa_thread_atexit_impl, which is to be provided by the threading library. |
138 | // The semantics are very similar to the __cxa_atexit function except for the |
139 | // fact that the registered callback is thread specific. |
140 | extern "C" int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj, |
141 | void *) { |
142 | return atexit_callback_mgr.add_callback(callback, obj); |
143 | } |
144 | |
145 | namespace internal { |
146 | |
147 | ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() { |
148 | return &atexit_callback_mgr; |
149 | } |
150 | |
151 | void call_atexit_callbacks(ThreadAttributes *attrib) { |
152 | attrib->atexit_callback_mgr->call(); |
153 | for (size_t i = 0; i < TSS_KEY_COUNT; ++i) { |
154 | TSSValueUnit &unit = tss_values[i]; |
155 | // Both dtor and value need to nonnull to call dtor |
156 | if (unit.dtor != nullptr && unit.payload != nullptr) |
157 | unit.dtor(unit.payload); |
158 | } |
159 | } |
160 | |
161 | } // namespace internal |
162 | |
163 | cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) { |
164 | return tss_key_mgr.new_key(dtor); |
165 | } |
166 | |
167 | bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); } |
168 | |
169 | bool set_tss_value(unsigned int key, void *val) { |
170 | if (!tss_key_mgr.is_valid_key(key)) |
171 | return false; |
172 | tss_values[key] = {val, tss_key_mgr.get_dtor(key)}; |
173 | return true; |
174 | } |
175 | |
176 | void *get_tss_value(unsigned int key) { |
177 | if (key >= TSS_KEY_COUNT) |
178 | return nullptr; |
179 | |
180 | auto &u = tss_values[key]; |
181 | if (!u.active) |
182 | return nullptr; |
183 | return u.payload; |
184 | } |
185 | |
186 | } // namespace LIBC_NAMESPACE |
187 | |