194 lines · cpp
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-exception6//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_guard15#include "src/__support/CPP/optional.h"16#include "src/__support/fixedvector.h"17#include "src/__support/macros/attributes.h"18 19namespace LIBC_NAMESPACE_DECL {20namespace {21 22using AtExitCallback = void(void *);23 24struct 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 31constexpr size_t TSS_KEY_COUNT = 1024;32 33struct TSSKeyUnit {34 // Indicates whether is unit is active. Presence of a non-null dtor35 // is not sufficient to indicate the same information as a TSS key can36 // 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 50class TSSKeyMgr {51 Mutex mtx;52 cpp::array<TSSKeyUnit, TSS_KEY_COUNT> units;53 54public: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 92TSSKeyMgr tss_key_mgr;93 94struct 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 104static LIBC_THREAD_LOCAL cpp::array<TSSValueUnit, TSS_KEY_COUNT> tss_values;105 106} // anonymous namespace107 108class ThreadAtExitCallbackMgr {109 Mutex mtx;110 // TODO: Use a BlockStore when compiled for production.111 FixedVector<AtExitUnit, 1024> callback_list;112 113public: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 137static 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. To141// actually register destructor call with the threading library, it calls142// __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 the144// fact that the registered callback is thread specific.145extern "C" int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj,146 void *) {147 return atexit_callback_mgr.add_callback(callback, obj);148}149 150namespace internal {151 152ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() {153 return &atexit_callback_mgr;154}155 156void 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 dtor161 if (unit.dtor != nullptr && unit.payload != nullptr)162 unit.dtor(unit.payload);163 }164}165 166extern "C" void __cxa_thread_finalize() { call_atexit_callbacks(self.attrib); }167 168} // namespace internal169 170cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) {171 return tss_key_mgr.new_key(dtor);172}173 174bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); }175 176bool set_tss_value(unsigned int key, void *val) {177 if (!tss_key_mgr.is_valid_key(key))178 return false;179 tss_values[key] = {val, tss_key_mgr.get_dtor(key)};180 return true;181}182 183void *get_tss_value(unsigned int key) {184 if (key >= TSS_KEY_COUNT)185 return nullptr;186 187 auto &u = tss_values[key];188 if (!u.active)189 return nullptr;190 return u.payload;191}192 193} // namespace LIBC_NAMESPACE_DECL194