1*c05d8e5dSAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
2*c05d8e5dSAndroid Build Coastguard Worker //
3*c05d8e5dSAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*c05d8e5dSAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*c05d8e5dSAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*c05d8e5dSAndroid Build Coastguard Worker //
7*c05d8e5dSAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*c05d8e5dSAndroid Build Coastguard Worker
9*c05d8e5dSAndroid Build Coastguard Worker // UNSUPPORTED: c++98, c++03
10*c05d8e5dSAndroid Build Coastguard Worker // UNSUPPORTED: libcxxabi-no-threads, libcxxabi-no-exceptions
11*c05d8e5dSAndroid Build Coastguard Worker
12*c05d8e5dSAndroid Build Coastguard Worker #define TESTING_CXA_GUARD
13*c05d8e5dSAndroid Build Coastguard Worker #include "../src/cxa_guard_impl.h"
14*c05d8e5dSAndroid Build Coastguard Worker #include <unordered_map>
15*c05d8e5dSAndroid Build Coastguard Worker #include <thread>
16*c05d8e5dSAndroid Build Coastguard Worker #include <atomic>
17*c05d8e5dSAndroid Build Coastguard Worker #include <array>
18*c05d8e5dSAndroid Build Coastguard Worker #include <cassert>
19*c05d8e5dSAndroid Build Coastguard Worker #include <memory>
20*c05d8e5dSAndroid Build Coastguard Worker #include <vector>
21*c05d8e5dSAndroid Build Coastguard Worker
22*c05d8e5dSAndroid Build Coastguard Worker
23*c05d8e5dSAndroid Build Coastguard Worker using namespace __cxxabiv1;
24*c05d8e5dSAndroid Build Coastguard Worker
25*c05d8e5dSAndroid Build Coastguard Worker enum class InitResult {
26*c05d8e5dSAndroid Build Coastguard Worker COMPLETE,
27*c05d8e5dSAndroid Build Coastguard Worker PERFORMED,
28*c05d8e5dSAndroid Build Coastguard Worker WAITED,
29*c05d8e5dSAndroid Build Coastguard Worker ABORTED
30*c05d8e5dSAndroid Build Coastguard Worker };
31*c05d8e5dSAndroid Build Coastguard Worker constexpr InitResult COMPLETE = InitResult::COMPLETE;
32*c05d8e5dSAndroid Build Coastguard Worker constexpr InitResult PERFORMED = InitResult::PERFORMED;
33*c05d8e5dSAndroid Build Coastguard Worker constexpr InitResult WAITED = InitResult::WAITED;
34*c05d8e5dSAndroid Build Coastguard Worker constexpr InitResult ABORTED = InitResult::ABORTED;
35*c05d8e5dSAndroid Build Coastguard Worker
36*c05d8e5dSAndroid Build Coastguard Worker
37*c05d8e5dSAndroid Build Coastguard Worker template <class Impl, class GuardType, class Init>
check_guard(GuardType * g,Init init)38*c05d8e5dSAndroid Build Coastguard Worker InitResult check_guard(GuardType *g, Init init) {
39*c05d8e5dSAndroid Build Coastguard Worker uint8_t *first_byte = reinterpret_cast<uint8_t*>(g);
40*c05d8e5dSAndroid Build Coastguard Worker if (std::__libcpp_atomic_load(first_byte, std::_AO_Acquire) == 0) {
41*c05d8e5dSAndroid Build Coastguard Worker Impl impl(g);
42*c05d8e5dSAndroid Build Coastguard Worker if (impl.cxa_guard_acquire() == INIT_IS_PENDING) {
43*c05d8e5dSAndroid Build Coastguard Worker #ifndef LIBCXXABI_HAS_NO_EXCEPTIONS
44*c05d8e5dSAndroid Build Coastguard Worker try {
45*c05d8e5dSAndroid Build Coastguard Worker #endif
46*c05d8e5dSAndroid Build Coastguard Worker init();
47*c05d8e5dSAndroid Build Coastguard Worker impl.cxa_guard_release();
48*c05d8e5dSAndroid Build Coastguard Worker return PERFORMED;
49*c05d8e5dSAndroid Build Coastguard Worker #ifndef LIBCXXABI_HAS_NO_EXCEPTIONS
50*c05d8e5dSAndroid Build Coastguard Worker } catch (...) {
51*c05d8e5dSAndroid Build Coastguard Worker impl.cxa_guard_abort();
52*c05d8e5dSAndroid Build Coastguard Worker return ABORTED;
53*c05d8e5dSAndroid Build Coastguard Worker }
54*c05d8e5dSAndroid Build Coastguard Worker #endif
55*c05d8e5dSAndroid Build Coastguard Worker }
56*c05d8e5dSAndroid Build Coastguard Worker return WAITED;
57*c05d8e5dSAndroid Build Coastguard Worker }
58*c05d8e5dSAndroid Build Coastguard Worker return COMPLETE;
59*c05d8e5dSAndroid Build Coastguard Worker }
60*c05d8e5dSAndroid Build Coastguard Worker
61*c05d8e5dSAndroid Build Coastguard Worker
62*c05d8e5dSAndroid Build Coastguard Worker template <class GuardType, class Impl>
63*c05d8e5dSAndroid Build Coastguard Worker struct FunctionLocalStatic {
FunctionLocalStaticFunctionLocalStatic64*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic() { reset(); }
65*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic(FunctionLocalStatic const&) = delete;
66*c05d8e5dSAndroid Build Coastguard Worker
67*c05d8e5dSAndroid Build Coastguard Worker template <class InitFunc>
accessFunctionLocalStatic68*c05d8e5dSAndroid Build Coastguard Worker InitResult access(InitFunc&& init) {
69*c05d8e5dSAndroid Build Coastguard Worker ++waiting_threads;
70*c05d8e5dSAndroid Build Coastguard Worker auto res = check_guard<Impl>(&guard_object, init);
71*c05d8e5dSAndroid Build Coastguard Worker --waiting_threads;
72*c05d8e5dSAndroid Build Coastguard Worker ++result_counts[static_cast<int>(res)];
73*c05d8e5dSAndroid Build Coastguard Worker return res;
74*c05d8e5dSAndroid Build Coastguard Worker }
75*c05d8e5dSAndroid Build Coastguard Worker
76*c05d8e5dSAndroid Build Coastguard Worker struct Accessor {
AccessorFunctionLocalStatic::Accessor77*c05d8e5dSAndroid Build Coastguard Worker explicit Accessor(FunctionLocalStatic& obj) : this_obj(&obj) {}
78*c05d8e5dSAndroid Build Coastguard Worker
79*c05d8e5dSAndroid Build Coastguard Worker template <class InitFn>
operator ()FunctionLocalStatic::Accessor80*c05d8e5dSAndroid Build Coastguard Worker void operator()(InitFn && fn) const {
81*c05d8e5dSAndroid Build Coastguard Worker this_obj->access(std::forward<InitFn>(fn));
82*c05d8e5dSAndroid Build Coastguard Worker }
83*c05d8e5dSAndroid Build Coastguard Worker private:
84*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic *this_obj;
85*c05d8e5dSAndroid Build Coastguard Worker };
86*c05d8e5dSAndroid Build Coastguard Worker
get_accessFunctionLocalStatic87*c05d8e5dSAndroid Build Coastguard Worker Accessor get_access() {
88*c05d8e5dSAndroid Build Coastguard Worker return Accessor(*this);
89*c05d8e5dSAndroid Build Coastguard Worker }
90*c05d8e5dSAndroid Build Coastguard Worker
resetFunctionLocalStatic91*c05d8e5dSAndroid Build Coastguard Worker void reset() {
92*c05d8e5dSAndroid Build Coastguard Worker guard_object = 0;
93*c05d8e5dSAndroid Build Coastguard Worker waiting_threads.store(0);
94*c05d8e5dSAndroid Build Coastguard Worker for (auto& counter : result_counts) {
95*c05d8e5dSAndroid Build Coastguard Worker counter.store(0);
96*c05d8e5dSAndroid Build Coastguard Worker }
97*c05d8e5dSAndroid Build Coastguard Worker }
98*c05d8e5dSAndroid Build Coastguard Worker
get_countFunctionLocalStatic99*c05d8e5dSAndroid Build Coastguard Worker int get_count(InitResult I) const {
100*c05d8e5dSAndroid Build Coastguard Worker return result_counts[static_cast<int>(I)].load();
101*c05d8e5dSAndroid Build Coastguard Worker }
num_completedFunctionLocalStatic102*c05d8e5dSAndroid Build Coastguard Worker int num_completed() const {
103*c05d8e5dSAndroid Build Coastguard Worker return get_count(COMPLETE) + get_count(PERFORMED) + get_count(WAITED);
104*c05d8e5dSAndroid Build Coastguard Worker }
num_waitingFunctionLocalStatic105*c05d8e5dSAndroid Build Coastguard Worker int num_waiting() const {
106*c05d8e5dSAndroid Build Coastguard Worker return waiting_threads.load();
107*c05d8e5dSAndroid Build Coastguard Worker }
108*c05d8e5dSAndroid Build Coastguard Worker
109*c05d8e5dSAndroid Build Coastguard Worker private:
110*c05d8e5dSAndroid Build Coastguard Worker GuardType guard_object;
111*c05d8e5dSAndroid Build Coastguard Worker std::atomic<int> waiting_threads;
112*c05d8e5dSAndroid Build Coastguard Worker std::array<std::atomic<int>, 4> result_counts;
113*c05d8e5dSAndroid Build Coastguard Worker static_assert(static_cast<int>(ABORTED) == 3, "only 4 result kinds expected");
114*c05d8e5dSAndroid Build Coastguard Worker };
115*c05d8e5dSAndroid Build Coastguard Worker
116*c05d8e5dSAndroid Build Coastguard Worker struct ThreadGroup {
117*c05d8e5dSAndroid Build Coastguard Worker ThreadGroup() = default;
118*c05d8e5dSAndroid Build Coastguard Worker ThreadGroup(ThreadGroup const&) = delete;
119*c05d8e5dSAndroid Build Coastguard Worker
120*c05d8e5dSAndroid Build Coastguard Worker template <class ...Args>
CreateThreadGroup121*c05d8e5dSAndroid Build Coastguard Worker void Create(Args&& ...args) {
122*c05d8e5dSAndroid Build Coastguard Worker threads.emplace_back(std::forward<Args>(args)...);
123*c05d8e5dSAndroid Build Coastguard Worker }
124*c05d8e5dSAndroid Build Coastguard Worker
JoinAllThreadGroup125*c05d8e5dSAndroid Build Coastguard Worker void JoinAll() {
126*c05d8e5dSAndroid Build Coastguard Worker for (auto& t : threads) {
127*c05d8e5dSAndroid Build Coastguard Worker t.join();
128*c05d8e5dSAndroid Build Coastguard Worker }
129*c05d8e5dSAndroid Build Coastguard Worker }
130*c05d8e5dSAndroid Build Coastguard Worker
131*c05d8e5dSAndroid Build Coastguard Worker private:
132*c05d8e5dSAndroid Build Coastguard Worker std::vector<std::thread> threads;
133*c05d8e5dSAndroid Build Coastguard Worker };
134*c05d8e5dSAndroid Build Coastguard Worker
135*c05d8e5dSAndroid Build Coastguard Worker struct Barrier {
BarrierBarrier136*c05d8e5dSAndroid Build Coastguard Worker explicit Barrier(int n) : m_wait_for(n) { reset(); }
137*c05d8e5dSAndroid Build Coastguard Worker Barrier(Barrier const&) = delete;
138*c05d8e5dSAndroid Build Coastguard Worker
waitBarrier139*c05d8e5dSAndroid Build Coastguard Worker void wait() {
140*c05d8e5dSAndroid Build Coastguard Worker ++m_entered;
141*c05d8e5dSAndroid Build Coastguard Worker while (m_entered.load() < m_wait_for) {
142*c05d8e5dSAndroid Build Coastguard Worker std::this_thread::yield();
143*c05d8e5dSAndroid Build Coastguard Worker }
144*c05d8e5dSAndroid Build Coastguard Worker assert(m_entered.load() == m_wait_for);
145*c05d8e5dSAndroid Build Coastguard Worker ++m_exited;
146*c05d8e5dSAndroid Build Coastguard Worker }
147*c05d8e5dSAndroid Build Coastguard Worker
num_waitingBarrier148*c05d8e5dSAndroid Build Coastguard Worker int num_waiting() const {
149*c05d8e5dSAndroid Build Coastguard Worker return m_entered.load() - m_exited.load();
150*c05d8e5dSAndroid Build Coastguard Worker }
151*c05d8e5dSAndroid Build Coastguard Worker
resetBarrier152*c05d8e5dSAndroid Build Coastguard Worker void reset() {
153*c05d8e5dSAndroid Build Coastguard Worker m_entered.store(0);
154*c05d8e5dSAndroid Build Coastguard Worker m_exited.store(0);
155*c05d8e5dSAndroid Build Coastguard Worker }
156*c05d8e5dSAndroid Build Coastguard Worker private:
157*c05d8e5dSAndroid Build Coastguard Worker const int m_wait_for;
158*c05d8e5dSAndroid Build Coastguard Worker std::atomic<int> m_entered;
159*c05d8e5dSAndroid Build Coastguard Worker std::atomic<int> m_exited;
160*c05d8e5dSAndroid Build Coastguard Worker };
161*c05d8e5dSAndroid Build Coastguard Worker
162*c05d8e5dSAndroid Build Coastguard Worker struct Notification {
NotificationNotification163*c05d8e5dSAndroid Build Coastguard Worker Notification() { reset(); }
164*c05d8e5dSAndroid Build Coastguard Worker Notification(Notification const&) = delete;
165*c05d8e5dSAndroid Build Coastguard Worker
num_waitingNotification166*c05d8e5dSAndroid Build Coastguard Worker int num_waiting() const {
167*c05d8e5dSAndroid Build Coastguard Worker return m_waiting.load();
168*c05d8e5dSAndroid Build Coastguard Worker }
169*c05d8e5dSAndroid Build Coastguard Worker
waitNotification170*c05d8e5dSAndroid Build Coastguard Worker void wait() {
171*c05d8e5dSAndroid Build Coastguard Worker if (m_cond.load())
172*c05d8e5dSAndroid Build Coastguard Worker return;
173*c05d8e5dSAndroid Build Coastguard Worker ++m_waiting;
174*c05d8e5dSAndroid Build Coastguard Worker while (!m_cond.load()) {
175*c05d8e5dSAndroid Build Coastguard Worker std::this_thread::yield();
176*c05d8e5dSAndroid Build Coastguard Worker }
177*c05d8e5dSAndroid Build Coastguard Worker --m_waiting;
178*c05d8e5dSAndroid Build Coastguard Worker }
179*c05d8e5dSAndroid Build Coastguard Worker
notifyNotification180*c05d8e5dSAndroid Build Coastguard Worker void notify() {
181*c05d8e5dSAndroid Build Coastguard Worker m_cond.store(true);
182*c05d8e5dSAndroid Build Coastguard Worker }
183*c05d8e5dSAndroid Build Coastguard Worker
184*c05d8e5dSAndroid Build Coastguard Worker template <class Cond>
notify_whenNotification185*c05d8e5dSAndroid Build Coastguard Worker void notify_when(Cond &&c) {
186*c05d8e5dSAndroid Build Coastguard Worker if (m_cond.load())
187*c05d8e5dSAndroid Build Coastguard Worker return;
188*c05d8e5dSAndroid Build Coastguard Worker while (!c()) {
189*c05d8e5dSAndroid Build Coastguard Worker std::this_thread::yield();
190*c05d8e5dSAndroid Build Coastguard Worker }
191*c05d8e5dSAndroid Build Coastguard Worker m_cond.store(true);
192*c05d8e5dSAndroid Build Coastguard Worker }
193*c05d8e5dSAndroid Build Coastguard Worker
resetNotification194*c05d8e5dSAndroid Build Coastguard Worker void reset() {
195*c05d8e5dSAndroid Build Coastguard Worker m_cond.store(0);
196*c05d8e5dSAndroid Build Coastguard Worker m_waiting.store(0);
197*c05d8e5dSAndroid Build Coastguard Worker }
198*c05d8e5dSAndroid Build Coastguard Worker private:
199*c05d8e5dSAndroid Build Coastguard Worker std::atomic<bool> m_cond;
200*c05d8e5dSAndroid Build Coastguard Worker std::atomic<int> m_waiting;
201*c05d8e5dSAndroid Build Coastguard Worker };
202*c05d8e5dSAndroid Build Coastguard Worker
203*c05d8e5dSAndroid Build Coastguard Worker
204*c05d8e5dSAndroid Build Coastguard Worker template <class GuardType, class Impl>
test_free_for_all()205*c05d8e5dSAndroid Build Coastguard Worker void test_free_for_all() {
206*c05d8e5dSAndroid Build Coastguard Worker const int num_waiting_threads = 10; // one initializing thread, 10 waiters.
207*c05d8e5dSAndroid Build Coastguard Worker
208*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic<GuardType, Impl> test_obj;
209*c05d8e5dSAndroid Build Coastguard Worker
210*c05d8e5dSAndroid Build Coastguard Worker Barrier start_init_barrier(num_waiting_threads);
211*c05d8e5dSAndroid Build Coastguard Worker bool already_init = false;
212*c05d8e5dSAndroid Build Coastguard Worker ThreadGroup threads;
213*c05d8e5dSAndroid Build Coastguard Worker for (int i=0; i < num_waiting_threads; ++i) {
214*c05d8e5dSAndroid Build Coastguard Worker threads.Create([&]() {
215*c05d8e5dSAndroid Build Coastguard Worker start_init_barrier.wait();
216*c05d8e5dSAndroid Build Coastguard Worker test_obj.access([&]() {
217*c05d8e5dSAndroid Build Coastguard Worker assert(!already_init);
218*c05d8e5dSAndroid Build Coastguard Worker already_init = true;
219*c05d8e5dSAndroid Build Coastguard Worker });
220*c05d8e5dSAndroid Build Coastguard Worker });
221*c05d8e5dSAndroid Build Coastguard Worker }
222*c05d8e5dSAndroid Build Coastguard Worker
223*c05d8e5dSAndroid Build Coastguard Worker // wait for the other threads to finish initialization.
224*c05d8e5dSAndroid Build Coastguard Worker threads.JoinAll();
225*c05d8e5dSAndroid Build Coastguard Worker
226*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(PERFORMED) == 1);
227*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(COMPLETE) + test_obj.get_count(WAITED) == 9);
228*c05d8e5dSAndroid Build Coastguard Worker }
229*c05d8e5dSAndroid Build Coastguard Worker
230*c05d8e5dSAndroid Build Coastguard Worker template <class GuardType, class Impl>
test_waiting_for_init()231*c05d8e5dSAndroid Build Coastguard Worker void test_waiting_for_init() {
232*c05d8e5dSAndroid Build Coastguard Worker const int num_waiting_threads = 10; // one initializing thread, 10 waiters.
233*c05d8e5dSAndroid Build Coastguard Worker
234*c05d8e5dSAndroid Build Coastguard Worker Notification init_pending;
235*c05d8e5dSAndroid Build Coastguard Worker Notification init_barrier;
236*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic<GuardType, Impl> test_obj;
237*c05d8e5dSAndroid Build Coastguard Worker auto access_fn = test_obj.get_access();
238*c05d8e5dSAndroid Build Coastguard Worker
239*c05d8e5dSAndroid Build Coastguard Worker ThreadGroup threads;
240*c05d8e5dSAndroid Build Coastguard Worker threads.Create(access_fn,
241*c05d8e5dSAndroid Build Coastguard Worker [&]() {
242*c05d8e5dSAndroid Build Coastguard Worker init_pending.notify();
243*c05d8e5dSAndroid Build Coastguard Worker init_barrier.wait();
244*c05d8e5dSAndroid Build Coastguard Worker }
245*c05d8e5dSAndroid Build Coastguard Worker );
246*c05d8e5dSAndroid Build Coastguard Worker init_pending.wait();
247*c05d8e5dSAndroid Build Coastguard Worker
248*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.num_waiting() == 1);
249*c05d8e5dSAndroid Build Coastguard Worker
250*c05d8e5dSAndroid Build Coastguard Worker for (int i=0; i < num_waiting_threads; ++i) {
251*c05d8e5dSAndroid Build Coastguard Worker threads.Create(access_fn, []() { assert(false); });
252*c05d8e5dSAndroid Build Coastguard Worker }
253*c05d8e5dSAndroid Build Coastguard Worker // unblock the initializing thread
254*c05d8e5dSAndroid Build Coastguard Worker init_barrier.notify_when([&]() {
255*c05d8e5dSAndroid Build Coastguard Worker return test_obj.num_waiting() == num_waiting_threads + 1;
256*c05d8e5dSAndroid Build Coastguard Worker });
257*c05d8e5dSAndroid Build Coastguard Worker
258*c05d8e5dSAndroid Build Coastguard Worker // wait for the other threads to finish initialization.
259*c05d8e5dSAndroid Build Coastguard Worker threads.JoinAll();
260*c05d8e5dSAndroid Build Coastguard Worker
261*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(PERFORMED) == 1);
262*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(WAITED) == 10);
263*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(COMPLETE) == 0);
264*c05d8e5dSAndroid Build Coastguard Worker }
265*c05d8e5dSAndroid Build Coastguard Worker
266*c05d8e5dSAndroid Build Coastguard Worker
267*c05d8e5dSAndroid Build Coastguard Worker template <class GuardType, class Impl>
test_aborted_init()268*c05d8e5dSAndroid Build Coastguard Worker void test_aborted_init() {
269*c05d8e5dSAndroid Build Coastguard Worker const int num_waiting_threads = 10; // one initializing thread, 10 waiters.
270*c05d8e5dSAndroid Build Coastguard Worker
271*c05d8e5dSAndroid Build Coastguard Worker Notification init_pending;
272*c05d8e5dSAndroid Build Coastguard Worker Notification init_barrier;
273*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic<GuardType, Impl> test_obj;
274*c05d8e5dSAndroid Build Coastguard Worker auto access_fn = test_obj.get_access();
275*c05d8e5dSAndroid Build Coastguard Worker
276*c05d8e5dSAndroid Build Coastguard Worker ThreadGroup threads;
277*c05d8e5dSAndroid Build Coastguard Worker threads.Create(access_fn,
278*c05d8e5dSAndroid Build Coastguard Worker [&]() {
279*c05d8e5dSAndroid Build Coastguard Worker init_pending.notify();
280*c05d8e5dSAndroid Build Coastguard Worker init_barrier.wait();
281*c05d8e5dSAndroid Build Coastguard Worker throw 42;
282*c05d8e5dSAndroid Build Coastguard Worker }
283*c05d8e5dSAndroid Build Coastguard Worker );
284*c05d8e5dSAndroid Build Coastguard Worker init_pending.wait();
285*c05d8e5dSAndroid Build Coastguard Worker
286*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.num_waiting() == 1);
287*c05d8e5dSAndroid Build Coastguard Worker
288*c05d8e5dSAndroid Build Coastguard Worker bool already_init = false;
289*c05d8e5dSAndroid Build Coastguard Worker for (int i=0; i < num_waiting_threads; ++i) {
290*c05d8e5dSAndroid Build Coastguard Worker threads.Create(access_fn, [&]() {
291*c05d8e5dSAndroid Build Coastguard Worker assert(!already_init);
292*c05d8e5dSAndroid Build Coastguard Worker already_init = true;
293*c05d8e5dSAndroid Build Coastguard Worker });
294*c05d8e5dSAndroid Build Coastguard Worker }
295*c05d8e5dSAndroid Build Coastguard Worker // unblock the initializing thread
296*c05d8e5dSAndroid Build Coastguard Worker init_barrier.notify_when([&]() {
297*c05d8e5dSAndroid Build Coastguard Worker return test_obj.num_waiting() == num_waiting_threads + 1;
298*c05d8e5dSAndroid Build Coastguard Worker });
299*c05d8e5dSAndroid Build Coastguard Worker
300*c05d8e5dSAndroid Build Coastguard Worker // wait for the other threads to finish initialization.
301*c05d8e5dSAndroid Build Coastguard Worker threads.JoinAll();
302*c05d8e5dSAndroid Build Coastguard Worker
303*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(ABORTED) == 1);
304*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(PERFORMED) == 1);
305*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(WAITED) == 9);
306*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(COMPLETE) == 0);
307*c05d8e5dSAndroid Build Coastguard Worker }
308*c05d8e5dSAndroid Build Coastguard Worker
309*c05d8e5dSAndroid Build Coastguard Worker
310*c05d8e5dSAndroid Build Coastguard Worker template <class GuardType, class Impl>
test_completed_init()311*c05d8e5dSAndroid Build Coastguard Worker void test_completed_init() {
312*c05d8e5dSAndroid Build Coastguard Worker const int num_waiting_threads = 10; // one initializing thread, 10 waiters.
313*c05d8e5dSAndroid Build Coastguard Worker
314*c05d8e5dSAndroid Build Coastguard Worker Notification init_barrier;
315*c05d8e5dSAndroid Build Coastguard Worker FunctionLocalStatic<GuardType, Impl> test_obj;
316*c05d8e5dSAndroid Build Coastguard Worker
317*c05d8e5dSAndroid Build Coastguard Worker test_obj.access([]() {});
318*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.num_waiting() == 0);
319*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.num_completed() == 1);
320*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(PERFORMED) == 1);
321*c05d8e5dSAndroid Build Coastguard Worker
322*c05d8e5dSAndroid Build Coastguard Worker auto access_fn = test_obj.get_access();
323*c05d8e5dSAndroid Build Coastguard Worker ThreadGroup threads;
324*c05d8e5dSAndroid Build Coastguard Worker for (int i=0; i < num_waiting_threads; ++i) {
325*c05d8e5dSAndroid Build Coastguard Worker threads.Create(access_fn, []() {
326*c05d8e5dSAndroid Build Coastguard Worker assert(false);
327*c05d8e5dSAndroid Build Coastguard Worker });
328*c05d8e5dSAndroid Build Coastguard Worker }
329*c05d8e5dSAndroid Build Coastguard Worker
330*c05d8e5dSAndroid Build Coastguard Worker // wait for the other threads to finish initialization.
331*c05d8e5dSAndroid Build Coastguard Worker threads.JoinAll();
332*c05d8e5dSAndroid Build Coastguard Worker
333*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(ABORTED) == 0);
334*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(PERFORMED) == 1);
335*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(WAITED) == 0);
336*c05d8e5dSAndroid Build Coastguard Worker assert(test_obj.get_count(COMPLETE) == 10);
337*c05d8e5dSAndroid Build Coastguard Worker }
338*c05d8e5dSAndroid Build Coastguard Worker
339*c05d8e5dSAndroid Build Coastguard Worker template <class Impl>
test_impl()340*c05d8e5dSAndroid Build Coastguard Worker void test_impl() {
341*c05d8e5dSAndroid Build Coastguard Worker {
342*c05d8e5dSAndroid Build Coastguard Worker test_free_for_all<uint32_t, Impl>();
343*c05d8e5dSAndroid Build Coastguard Worker test_free_for_all<uint32_t, Impl>();
344*c05d8e5dSAndroid Build Coastguard Worker }
345*c05d8e5dSAndroid Build Coastguard Worker {
346*c05d8e5dSAndroid Build Coastguard Worker test_waiting_for_init<uint32_t, Impl>();
347*c05d8e5dSAndroid Build Coastguard Worker test_waiting_for_init<uint64_t, Impl>();
348*c05d8e5dSAndroid Build Coastguard Worker }
349*c05d8e5dSAndroid Build Coastguard Worker {
350*c05d8e5dSAndroid Build Coastguard Worker test_aborted_init<uint32_t, Impl>();
351*c05d8e5dSAndroid Build Coastguard Worker test_aborted_init<uint64_t, Impl>();
352*c05d8e5dSAndroid Build Coastguard Worker }
353*c05d8e5dSAndroid Build Coastguard Worker {
354*c05d8e5dSAndroid Build Coastguard Worker test_completed_init<uint32_t, Impl>();
355*c05d8e5dSAndroid Build Coastguard Worker test_completed_init<uint64_t, Impl>();
356*c05d8e5dSAndroid Build Coastguard Worker }
357*c05d8e5dSAndroid Build Coastguard Worker }
358*c05d8e5dSAndroid Build Coastguard Worker
test_all_impls()359*c05d8e5dSAndroid Build Coastguard Worker void test_all_impls() {
360*c05d8e5dSAndroid Build Coastguard Worker using MutexImpl = SelectImplementation<Implementation::GlobalLock>::type;
361*c05d8e5dSAndroid Build Coastguard Worker
362*c05d8e5dSAndroid Build Coastguard Worker // Attempt to test the Futex based implementation if it's supported on the
363*c05d8e5dSAndroid Build Coastguard Worker // target platform.
364*c05d8e5dSAndroid Build Coastguard Worker using RealFutexImpl = SelectImplementation<Implementation::Futex>::type;
365*c05d8e5dSAndroid Build Coastguard Worker using FutexImpl = typename std::conditional<
366*c05d8e5dSAndroid Build Coastguard Worker PlatformSupportsFutex(),
367*c05d8e5dSAndroid Build Coastguard Worker RealFutexImpl,
368*c05d8e5dSAndroid Build Coastguard Worker MutexImpl
369*c05d8e5dSAndroid Build Coastguard Worker >::type;
370*c05d8e5dSAndroid Build Coastguard Worker
371*c05d8e5dSAndroid Build Coastguard Worker // Run each test 5 times to help TSAN catch bugs.
372*c05d8e5dSAndroid Build Coastguard Worker const int num_runs = 5;
373*c05d8e5dSAndroid Build Coastguard Worker for (int i=0; i < num_runs; ++i) {
374*c05d8e5dSAndroid Build Coastguard Worker test_impl<MutexImpl>();
375*c05d8e5dSAndroid Build Coastguard Worker if (PlatformSupportsFutex())
376*c05d8e5dSAndroid Build Coastguard Worker test_impl<FutexImpl>();
377*c05d8e5dSAndroid Build Coastguard Worker }
378*c05d8e5dSAndroid Build Coastguard Worker }
379*c05d8e5dSAndroid Build Coastguard Worker
380*c05d8e5dSAndroid Build Coastguard Worker // A dummy
381*c05d8e5dSAndroid Build Coastguard Worker template <bool Dummy = true>
test_futex_syscall()382*c05d8e5dSAndroid Build Coastguard Worker void test_futex_syscall() {
383*c05d8e5dSAndroid Build Coastguard Worker if (!PlatformSupportsFutex())
384*c05d8e5dSAndroid Build Coastguard Worker return;
385*c05d8e5dSAndroid Build Coastguard Worker int lock1 = 0;
386*c05d8e5dSAndroid Build Coastguard Worker int lock2 = 0;
387*c05d8e5dSAndroid Build Coastguard Worker int lock3 = 0;
388*c05d8e5dSAndroid Build Coastguard Worker std::thread waiter1([&]() {
389*c05d8e5dSAndroid Build Coastguard Worker int expect = 0;
390*c05d8e5dSAndroid Build Coastguard Worker PlatformFutexWait(&lock1, expect);
391*c05d8e5dSAndroid Build Coastguard Worker assert(lock1 == 1);
392*c05d8e5dSAndroid Build Coastguard Worker });
393*c05d8e5dSAndroid Build Coastguard Worker std::thread waiter2([&]() {
394*c05d8e5dSAndroid Build Coastguard Worker int expect = 0;
395*c05d8e5dSAndroid Build Coastguard Worker PlatformFutexWait(&lock2, expect);
396*c05d8e5dSAndroid Build Coastguard Worker assert(lock2 == 2);
397*c05d8e5dSAndroid Build Coastguard Worker });
398*c05d8e5dSAndroid Build Coastguard Worker std::thread waiter3([&]() {
399*c05d8e5dSAndroid Build Coastguard Worker int expect = 42; // not the value
400*c05d8e5dSAndroid Build Coastguard Worker PlatformFutexWait(&lock3, expect); // doesn't block
401*c05d8e5dSAndroid Build Coastguard Worker });
402*c05d8e5dSAndroid Build Coastguard Worker std::thread waker([&]() {
403*c05d8e5dSAndroid Build Coastguard Worker lock1 = 1;
404*c05d8e5dSAndroid Build Coastguard Worker PlatformFutexWake(&lock1);
405*c05d8e5dSAndroid Build Coastguard Worker lock2 = 2;
406*c05d8e5dSAndroid Build Coastguard Worker PlatformFutexWake(&lock2);
407*c05d8e5dSAndroid Build Coastguard Worker });
408*c05d8e5dSAndroid Build Coastguard Worker waiter1.join();
409*c05d8e5dSAndroid Build Coastguard Worker waiter2.join();
410*c05d8e5dSAndroid Build Coastguard Worker waiter3.join();
411*c05d8e5dSAndroid Build Coastguard Worker waker.join();
412*c05d8e5dSAndroid Build Coastguard Worker }
413*c05d8e5dSAndroid Build Coastguard Worker
main()414*c05d8e5dSAndroid Build Coastguard Worker int main() {
415*c05d8e5dSAndroid Build Coastguard Worker // Test each multi-threaded implementation with real threads.
416*c05d8e5dSAndroid Build Coastguard Worker test_all_impls();
417*c05d8e5dSAndroid Build Coastguard Worker // Test the basic sanity of the futex syscall wrappers.
418*c05d8e5dSAndroid Build Coastguard Worker test_futex_syscall();
419*c05d8e5dSAndroid Build Coastguard Worker }
420