[NFC][Py Reformat] Added more commits to .git-blame-ignore-revs
[llvm-project.git] / libc / src / __support / threads / thread.cpp
blob03e93a42fb04caa33d18a9b73e21b0ac156273d4
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 //===----------------------------------------------------------------------===//
9 #include "thread.h"
10 #include "mutex.h"
12 #include "src/__support/CPP/array.h"
13 #include "src/__support/CPP/optional.h"
14 #include "src/__support/fixedvector.h"
16 namespace __llvm_libc {
18 thread_local Thread self;
20 namespace {
22 using AtExitCallback = void(void *);
24 struct AtExitUnit {
25 AtExitCallback *callback = nullptr;
26 void *obj = nullptr;
27 constexpr AtExitUnit() = default;
28 constexpr AtExitUnit(AtExitCallback *cb, void *o) : callback(cb), obj(o) {}
31 constexpr size_t TSS_KEY_COUNT = 1024;
33 struct TSSKeyUnit {
34 // Indicates whether is unit is active. Presence of a non-null dtor
35 // is not sufficient to indicate the same information as a TSS key can
36 // have a null destructor.
37 bool active = false;
39 TSSDtor *dtor = nullptr;
41 constexpr TSSKeyUnit() = default;
42 constexpr TSSKeyUnit(TSSDtor *d) : active(true), dtor(d) {}
44 void reset() {
45 active = false;
46 dtor = nullptr;
50 class TSSKeyMgr {
51 Mutex mtx;
52 cpp::array<TSSKeyUnit, TSS_KEY_COUNT> units;
54 public:
55 constexpr TSSKeyMgr() : mtx(false, false, false) {}
57 cpp::optional<unsigned int> new_key(TSSDtor *dtor) {
58 MutexLock lock(&mtx);
59 for (size_t i = 0; i < TSS_KEY_COUNT; ++i) {
60 TSSKeyUnit &u = units[i];
61 if (!u.active) {
62 u = {dtor};
63 return i;
66 return cpp::optional<unsigned int>();
69 TSSDtor *get_dtor(unsigned int key) {
70 if (key >= TSS_KEY_COUNT)
71 return nullptr;
72 MutexLock lock(&mtx);
73 return units[key].dtor;
76 bool remove_key(unsigned int key) {
77 if (key >= TSS_KEY_COUNT)
78 return false;
79 MutexLock lock(&mtx);
80 units[key].reset();
81 return true;
84 bool is_valid_key(unsigned int key) {
85 MutexLock lock(&mtx);
86 return units[key].active;
90 TSSKeyMgr tss_key_mgr;
92 struct TSSValueUnit {
93 bool active = false;
94 void *payload = nullptr;
95 TSSDtor *dtor = nullptr;
97 constexpr TSSValueUnit() = default;
98 constexpr TSSValueUnit(void *p, TSSDtor *d)
99 : active(true), payload(p), dtor(d) {}
102 static thread_local cpp::array<TSSValueUnit, TSS_KEY_COUNT> tss_values;
104 } // anonymous namespace
106 class ThreadAtExitCallbackMgr {
107 Mutex mtx;
108 // TODO: Use a BlockStore when compiled for production.
109 FixedVector<AtExitUnit, 1024> callback_list;
111 public:
112 constexpr ThreadAtExitCallbackMgr() : mtx(false, false, false) {}
114 int add_callback(AtExitCallback *callback, void *obj) {
115 MutexLock lock(&mtx);
116 return callback_list.push_back({callback, obj});
119 void call() {
120 mtx.lock();
121 while (!callback_list.empty()) {
122 auto atexit_unit = callback_list.back();
123 callback_list.pop_back();
124 mtx.unlock();
125 atexit_unit.callback(atexit_unit.obj);
126 mtx.lock();
131 static thread_local ThreadAtExitCallbackMgr atexit_callback_mgr;
133 // The function __cxa_thread_atexit is provided by C++ runtimes like libcxxabi.
134 // It is used by thread local object runtime to register destructor calls. To
135 // actually register destructor call with the threading library, it calls
136 // __cxa_thread_atexit_impl, which is to be provided by the threading library.
137 // The semantics are very similar to the __cxa_atexit function except for the
138 // fact that the registered callback is thread specific.
139 extern "C" int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj,
140 void *) {
141 return atexit_callback_mgr.add_callback(callback, obj);
144 namespace internal {
146 ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() {
147 return &atexit_callback_mgr;
150 void call_atexit_callbacks(ThreadAttributes *attrib) {
151 attrib->atexit_callback_mgr->call();
152 for (size_t i = 0; i < TSS_KEY_COUNT; ++i) {
153 TSSValueUnit &unit = tss_values[i];
154 // Both dtor and value need to nonnull to call dtor
155 if (unit.dtor != nullptr && unit.payload != nullptr)
156 unit.dtor(unit.payload);
160 } // namespace internal
162 cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) {
163 return tss_key_mgr.new_key(dtor);
166 bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); }
168 bool set_tss_value(unsigned int key, void *val) {
169 if (!tss_key_mgr.is_valid_key(key))
170 return false;
171 tss_values[key] = {val, tss_key_mgr.get_dtor(key)};
172 return true;
175 void *get_tss_value(unsigned int key) {
176 if (key >= TSS_KEY_COUNT)
177 return nullptr;
179 auto &u = tss_values[key];
180 if (!u.active)
181 return nullptr;
182 return u.payload;
185 } // namespace __llvm_libc