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; 28 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; 42 constexpr TSSKeyUnit(TSSDtor *d) : active(true), dtor(d) {} 43 44 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: 55 constexpr TSSKeyMgr() 56 : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false, 57 /*pshared=*/false) {} 58 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 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 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 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; 100 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: 114 constexpr ThreadAtExitCallbackMgr() 115 : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false, 116 /*pshared=*/false) {} 117 118 int add_callback(AtExitCallback *callback, void *obj) { 119 cpp::lock_guard lock(mtx); 120 if (callback_list.push_back({callback, obj})) 121 return 0; 122 return -1; 123 } 124 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. 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 152 ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() { 153 return &atexit_callback_mgr; 154 } 155 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 168 cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) { 169 return tss_key_mgr.new_key(dtor); 170 } 171 172 bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); } 173 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 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_DECL 192