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