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