xref: /aosp_15_r20/external/libcxxabi/test/guard_threaded_test.pass.cpp (revision c05d8e5dc3e10f6ce4317e8bc22cc4a25f55fa94)
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