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
17namespace LIBC_NAMESPACE {
18
19LIBC_THREAD_LOCAL Thread self;
20
21namespace {
22
23using AtExitCallback = void(void *);
24
25struct 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
32constexpr size_t TSS_KEY_COUNT = 1024;
33
34struct 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
51class TSSKeyMgr {
52 Mutex mtx;
53 cpp::array<TSSKeyUnit, TSS_KEY_COUNT> units;
54
55public:
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
91TSSKeyMgr tss_key_mgr;
92
93struct 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
103static LIBC_THREAD_LOCAL cpp::array<TSSValueUnit, TSS_KEY_COUNT> tss_values;
104
105} // anonymous namespace
106
107class ThreadAtExitCallbackMgr {
108 Mutex mtx;
109 // TODO: Use a BlockStore when compiled for production.
110 FixedVector<AtExitUnit, 1024> callback_list;
111
112public:
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
132static 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.
140extern "C" int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj,
141 void *) {
142 return atexit_callback_mgr.add_callback(callback, obj);
143}
144
145namespace internal {
146
147ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() {
148 return &atexit_callback_mgr;
149}
150
151void 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
163cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) {
164 return tss_key_mgr.new_key(dtor);
165}
166
167bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); }
168
169bool 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
176void *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

source code of libc/src/__support/threads/thread.cpp