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