xref: /llvm-project/libc/src/__support/threads/thread.cpp (revision b30f9d74d6a0f735ef597b1acae73daac2d7df39)
19c78d925SSiva Chandra Reddy //===--- Definitions of common thread items ---------------------*- C++ -*-===//
29c78d925SSiva Chandra Reddy //
39c78d925SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49c78d925SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
59c78d925SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69c78d925SSiva Chandra Reddy //
79c78d925SSiva Chandra Reddy //===----------------------------------------------------------------------===//
89c78d925SSiva Chandra Reddy 
9ab3a9e72SSchrodinger ZHU Yifan #include "src/__support/threads/thread.h"
105ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
11ab3a9e72SSchrodinger ZHU Yifan #include "src/__support/threads/mutex.h"
120071a795SSiva Chandra Reddy 
134a738ee8SSiva Chandra Reddy #include "src/__support/CPP/array.h"
14c4a3d184SVlad Mishel #include "src/__support/CPP/mutex.h" // lock_guard
154a738ee8SSiva Chandra Reddy #include "src/__support/CPP/optional.h"
160071a795SSiva Chandra Reddy #include "src/__support/fixedvector.h"
17daeee567SSiva Chandra Reddy #include "src/__support/macros/attributes.h"
188dc42802SSiva Chandra Reddy 
195ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
200071a795SSiva Chandra Reddy namespace {
210071a795SSiva Chandra Reddy 
220071a795SSiva Chandra Reddy using AtExitCallback = void(void *);
230071a795SSiva Chandra Reddy 
240071a795SSiva Chandra Reddy struct AtExitUnit {
250071a795SSiva Chandra Reddy   AtExitCallback *callback = nullptr;
260071a795SSiva Chandra Reddy   void *obj = nullptr;
270071a795SSiva Chandra Reddy   constexpr AtExitUnit() = default;
280071a795SSiva Chandra Reddy   constexpr AtExitUnit(AtExitCallback *cb, void *o) : callback(cb), obj(o) {}
290071a795SSiva Chandra Reddy };
300071a795SSiva Chandra Reddy 
314a738ee8SSiva Chandra Reddy constexpr size_t TSS_KEY_COUNT = 1024;
324a738ee8SSiva Chandra Reddy 
334a738ee8SSiva Chandra Reddy struct TSSKeyUnit {
344a738ee8SSiva Chandra Reddy   // Indicates whether is unit is active. Presence of a non-null dtor
354a738ee8SSiva Chandra Reddy   // is not sufficient to indicate the same information as a TSS key can
364a738ee8SSiva Chandra Reddy   // have a null destructor.
374a738ee8SSiva Chandra Reddy   bool active = false;
384a738ee8SSiva Chandra Reddy 
394a738ee8SSiva Chandra Reddy   TSSDtor *dtor = nullptr;
404a738ee8SSiva Chandra Reddy 
414a738ee8SSiva Chandra Reddy   constexpr TSSKeyUnit() = default;
424a738ee8SSiva Chandra Reddy   constexpr TSSKeyUnit(TSSDtor *d) : active(true), dtor(d) {}
434a738ee8SSiva Chandra Reddy 
444a738ee8SSiva Chandra Reddy   void reset() {
454a738ee8SSiva Chandra Reddy     active = false;
464a738ee8SSiva Chandra Reddy     dtor = nullptr;
474a738ee8SSiva Chandra Reddy   }
484a738ee8SSiva Chandra Reddy };
494a738ee8SSiva Chandra Reddy 
504a738ee8SSiva Chandra Reddy class TSSKeyMgr {
514a738ee8SSiva Chandra Reddy   Mutex mtx;
524a738ee8SSiva Chandra Reddy   cpp::array<TSSKeyUnit, TSS_KEY_COUNT> units;
534a738ee8SSiva Chandra Reddy 
544a738ee8SSiva Chandra Reddy public:
55142afde0SSchrodinger ZHU Yifan   constexpr TSSKeyMgr()
56142afde0SSchrodinger ZHU Yifan       : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false,
57142afde0SSchrodinger ZHU Yifan             /*pshared=*/false) {}
584a738ee8SSiva Chandra Reddy 
594a738ee8SSiva Chandra Reddy   cpp::optional<unsigned int> new_key(TSSDtor *dtor) {
60c4a3d184SVlad Mishel     cpp::lock_guard lock(mtx);
61f0a3954eSMichael Jones     for (unsigned int i = 0; i < TSS_KEY_COUNT; ++i) {
624a738ee8SSiva Chandra Reddy       TSSKeyUnit &u = units[i];
634a738ee8SSiva Chandra Reddy       if (!u.active) {
644a738ee8SSiva Chandra Reddy         u = {dtor};
654a738ee8SSiva Chandra Reddy         return i;
664a738ee8SSiva Chandra Reddy       }
674a738ee8SSiva Chandra Reddy     }
684a738ee8SSiva Chandra Reddy     return cpp::optional<unsigned int>();
694a738ee8SSiva Chandra Reddy   }
704a738ee8SSiva Chandra Reddy 
714a738ee8SSiva Chandra Reddy   TSSDtor *get_dtor(unsigned int key) {
724a738ee8SSiva Chandra Reddy     if (key >= TSS_KEY_COUNT)
734a738ee8SSiva Chandra Reddy       return nullptr;
74c4a3d184SVlad Mishel     cpp::lock_guard lock(mtx);
754a738ee8SSiva Chandra Reddy     return units[key].dtor;
764a738ee8SSiva Chandra Reddy   }
774a738ee8SSiva Chandra Reddy 
784a738ee8SSiva Chandra Reddy   bool remove_key(unsigned int key) {
794a738ee8SSiva Chandra Reddy     if (key >= TSS_KEY_COUNT)
804a738ee8SSiva Chandra Reddy       return false;
81c4a3d184SVlad Mishel     cpp::lock_guard lock(mtx);
824a738ee8SSiva Chandra Reddy     units[key].reset();
834a738ee8SSiva Chandra Reddy     return true;
844a738ee8SSiva Chandra Reddy   }
854a738ee8SSiva Chandra Reddy 
864a738ee8SSiva Chandra Reddy   bool is_valid_key(unsigned int key) {
87c4a3d184SVlad Mishel     cpp::lock_guard lock(mtx);
884a738ee8SSiva Chandra Reddy     return units[key].active;
894a738ee8SSiva Chandra Reddy   }
904a738ee8SSiva Chandra Reddy };
914a738ee8SSiva Chandra Reddy 
924a738ee8SSiva Chandra Reddy TSSKeyMgr tss_key_mgr;
934a738ee8SSiva Chandra Reddy 
944a738ee8SSiva Chandra Reddy struct TSSValueUnit {
954a738ee8SSiva Chandra Reddy   bool active = false;
964a738ee8SSiva Chandra Reddy   void *payload = nullptr;
974a738ee8SSiva Chandra Reddy   TSSDtor *dtor = nullptr;
984a738ee8SSiva Chandra Reddy 
994a738ee8SSiva Chandra Reddy   constexpr TSSValueUnit() = default;
1004a738ee8SSiva Chandra Reddy   constexpr TSSValueUnit(void *p, TSSDtor *d)
1014a738ee8SSiva Chandra Reddy       : active(true), payload(p), dtor(d) {}
1024a738ee8SSiva Chandra Reddy };
1034a738ee8SSiva Chandra Reddy 
104daeee567SSiva Chandra Reddy static LIBC_THREAD_LOCAL cpp::array<TSSValueUnit, TSS_KEY_COUNT> tss_values;
1054a738ee8SSiva Chandra Reddy 
1060071a795SSiva Chandra Reddy } // anonymous namespace
1070071a795SSiva Chandra Reddy 
1080071a795SSiva Chandra Reddy class ThreadAtExitCallbackMgr {
1090071a795SSiva Chandra Reddy   Mutex mtx;
1100071a795SSiva Chandra Reddy   // TODO: Use a BlockStore when compiled for production.
1110071a795SSiva Chandra Reddy   FixedVector<AtExitUnit, 1024> callback_list;
1120071a795SSiva Chandra Reddy 
1130071a795SSiva Chandra Reddy public:
114142afde0SSchrodinger ZHU Yifan   constexpr ThreadAtExitCallbackMgr()
115142afde0SSchrodinger ZHU Yifan       : mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false,
116142afde0SSchrodinger ZHU Yifan             /*pshared=*/false) {}
1170071a795SSiva Chandra Reddy 
1180071a795SSiva Chandra Reddy   int add_callback(AtExitCallback *callback, void *obj) {
119c4a3d184SVlad Mishel     cpp::lock_guard lock(mtx);
120*b30f9d74SAlexey Samsonov     if (callback_list.push_back({callback, obj}))
121*b30f9d74SAlexey Samsonov       return 0;
122*b30f9d74SAlexey Samsonov     return -1;
1230071a795SSiva Chandra Reddy   }
1240071a795SSiva Chandra Reddy 
1250071a795SSiva Chandra Reddy   void call() {
1260071a795SSiva Chandra Reddy     mtx.lock();
1270071a795SSiva Chandra Reddy     while (!callback_list.empty()) {
1280071a795SSiva Chandra Reddy       auto atexit_unit = callback_list.back();
1290071a795SSiva Chandra Reddy       callback_list.pop_back();
1300071a795SSiva Chandra Reddy       mtx.unlock();
1310071a795SSiva Chandra Reddy       atexit_unit.callback(atexit_unit.obj);
1320071a795SSiva Chandra Reddy       mtx.lock();
1330071a795SSiva Chandra Reddy     }
1340071a795SSiva Chandra Reddy   }
1350071a795SSiva Chandra Reddy };
1360071a795SSiva Chandra Reddy 
137daeee567SSiva Chandra Reddy static LIBC_THREAD_LOCAL ThreadAtExitCallbackMgr atexit_callback_mgr;
1380071a795SSiva Chandra Reddy 
1390071a795SSiva Chandra Reddy // The function __cxa_thread_atexit is provided by C++ runtimes like libcxxabi.
1400071a795SSiva Chandra Reddy // It is used by thread local object runtime to register destructor calls. To
1410071a795SSiva Chandra Reddy // actually register destructor call with the threading library, it calls
1420071a795SSiva Chandra Reddy // __cxa_thread_atexit_impl, which is to be provided by the threading library.
1430071a795SSiva Chandra Reddy // The semantics are very similar to the __cxa_atexit function except for the
1440071a795SSiva Chandra Reddy // fact that the registered callback is thread specific.
1450071a795SSiva Chandra Reddy extern "C" int __cxa_thread_atexit_impl(AtExitCallback *callback, void *obj,
1460071a795SSiva Chandra Reddy                                         void *) {
1470071a795SSiva Chandra Reddy   return atexit_callback_mgr.add_callback(callback, obj);
1480071a795SSiva Chandra Reddy }
1490071a795SSiva Chandra Reddy 
1500071a795SSiva Chandra Reddy namespace internal {
1510071a795SSiva Chandra Reddy 
1520071a795SSiva Chandra Reddy ThreadAtExitCallbackMgr *get_thread_atexit_callback_mgr() {
1530071a795SSiva Chandra Reddy   return &atexit_callback_mgr;
1540071a795SSiva Chandra Reddy }
1550071a795SSiva Chandra Reddy 
1560071a795SSiva Chandra Reddy void call_atexit_callbacks(ThreadAttributes *attrib) {
1570071a795SSiva Chandra Reddy   attrib->atexit_callback_mgr->call();
1584a738ee8SSiva Chandra Reddy   for (size_t i = 0; i < TSS_KEY_COUNT; ++i) {
1594a738ee8SSiva Chandra Reddy     TSSValueUnit &unit = tss_values[i];
1606f1a9ed0SNoah Goldstein     // Both dtor and value need to nonnull to call dtor
1616f1a9ed0SNoah Goldstein     if (unit.dtor != nullptr && unit.payload != nullptr)
1624a738ee8SSiva Chandra Reddy       unit.dtor(unit.payload);
1634a738ee8SSiva Chandra Reddy   }
1640071a795SSiva Chandra Reddy }
1650071a795SSiva Chandra Reddy 
1660071a795SSiva Chandra Reddy } // namespace internal
1670071a795SSiva Chandra Reddy 
1684a738ee8SSiva Chandra Reddy cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) {
1694a738ee8SSiva Chandra Reddy   return tss_key_mgr.new_key(dtor);
1704a738ee8SSiva Chandra Reddy }
1714a738ee8SSiva Chandra Reddy 
1724a738ee8SSiva Chandra Reddy bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); }
1734a738ee8SSiva Chandra Reddy 
1744a738ee8SSiva Chandra Reddy bool set_tss_value(unsigned int key, void *val) {
1754a738ee8SSiva Chandra Reddy   if (!tss_key_mgr.is_valid_key(key))
1764a738ee8SSiva Chandra Reddy     return false;
1774a738ee8SSiva Chandra Reddy   tss_values[key] = {val, tss_key_mgr.get_dtor(key)};
1784a738ee8SSiva Chandra Reddy   return true;
1794a738ee8SSiva Chandra Reddy }
1804a738ee8SSiva Chandra Reddy 
1814a738ee8SSiva Chandra Reddy void *get_tss_value(unsigned int key) {
1824a738ee8SSiva Chandra Reddy   if (key >= TSS_KEY_COUNT)
1834a738ee8SSiva Chandra Reddy     return nullptr;
1844a738ee8SSiva Chandra Reddy 
1854a738ee8SSiva Chandra Reddy   auto &u = tss_values[key];
1864a738ee8SSiva Chandra Reddy   if (!u.active)
1874a738ee8SSiva Chandra Reddy     return nullptr;
1884a738ee8SSiva Chandra Reddy   return u.payload;
1894a738ee8SSiva Chandra Reddy }
1904a738ee8SSiva Chandra Reddy 
1915ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
192