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
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 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
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 namespace
107
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. 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.
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 dtor
161 if (unit.dtor != nullptr && unit.payload != nullptr)
162 unit.dtor(unit.payload);
163 }
164}
165
166} // namespace internal
167
168cpp::optional<unsigned int> new_tss_key(TSSDtor *dtor) {
169 return tss_key_mgr.new_key(dtor);
170}
171
172bool tss_key_delete(unsigned int key) { return tss_key_mgr.remove_key(key); }
173
174bool 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
181void *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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of libc/src/__support/threads/thread.cpp