xref: /llvm-project/libcxxabi/test/guard_threaded_test.pass.cpp (revision a7f9895cc18995549c7facb96e72718da282a864)
170ebeabfSEric Fiselier //===----------------------------------------------------------------------===//
270ebeabfSEric Fiselier //
370ebeabfSEric Fiselier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
470ebeabfSEric Fiselier // See https://llvm.org/LICENSE.txt for license information.
570ebeabfSEric Fiselier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
670ebeabfSEric Fiselier //
770ebeabfSEric Fiselier //===----------------------------------------------------------------------===//
870ebeabfSEric Fiselier 
931cbe0f2SLouis Dionne // UNSUPPORTED: c++03
10*a7f9895cSLouis Dionne // UNSUPPORTED: no-threads
118c61114cSLouis Dionne // UNSUPPORTED: no-exceptions
1270ebeabfSEric Fiselier 
1370ebeabfSEric Fiselier #define TESTING_CXA_GUARD
1470ebeabfSEric Fiselier #include "../src/cxa_guard_impl.h"
1570ebeabfSEric Fiselier #include <unordered_map>
1670ebeabfSEric Fiselier #include <thread>
1770ebeabfSEric Fiselier #include <atomic>
1870ebeabfSEric Fiselier #include <array>
1970ebeabfSEric Fiselier #include <cassert>
2070ebeabfSEric Fiselier #include <memory>
2170ebeabfSEric Fiselier #include <vector>
2270ebeabfSEric Fiselier 
2356462801SLouis Dionne #include "make_test_thread.h"
24e6d94f4bSLouis Dionne #include "test_macros.h"
25e6d94f4bSLouis Dionne 
2670ebeabfSEric Fiselier 
2770ebeabfSEric Fiselier using namespace __cxxabiv1;
2870ebeabfSEric Fiselier 
29a4939d35SEric Fiselier // Misc test configuration. It's used to tune the flakyness of the test.
30a4939d35SEric Fiselier // ThreadsPerTest - The number of threads used
31a4939d35SEric Fiselier constexpr int ThreadsPerTest = 10;
32a4939d35SEric Fiselier // The number of instances of a test to run concurrently.
33a4939d35SEric Fiselier constexpr int ConcurrentRunsPerTest = 10;
34a4939d35SEric Fiselier // The number of times to rerun each test.
35a4939d35SEric Fiselier constexpr int TestSamples = 50;
36a4939d35SEric Fiselier 
37a4939d35SEric Fiselier 
38a4939d35SEric Fiselier 
BusyWait()39a4939d35SEric Fiselier void BusyWait() {
40a4939d35SEric Fiselier   std::this_thread::yield();
41a4939d35SEric Fiselier }
42a4939d35SEric Fiselier 
YieldAfterBarrier()43a4939d35SEric Fiselier void YieldAfterBarrier() {
44a4939d35SEric Fiselier   std::this_thread::sleep_for(std::chrono::nanoseconds(10));
45a4939d35SEric Fiselier   std::this_thread::yield();
46a4939d35SEric Fiselier }
47a4939d35SEric Fiselier 
48a4939d35SEric Fiselier struct Barrier {
BarrierBarrier49a4939d35SEric Fiselier   explicit Barrier(int n) : m_threads(n), m_remaining(n) { }
50a4939d35SEric Fiselier   Barrier(Barrier const&) = delete;
51a4939d35SEric Fiselier   Barrier& operator=(Barrier const&) = delete;
52a4939d35SEric Fiselier 
arrive_and_waitBarrier53a4939d35SEric Fiselier   void arrive_and_wait() const {
54a4939d35SEric Fiselier     --m_remaining;
55a4939d35SEric Fiselier     while (m_remaining.load()) {
56a4939d35SEric Fiselier       BusyWait();
57a4939d35SEric Fiselier     }
58a4939d35SEric Fiselier   }
59a4939d35SEric Fiselier 
arrive_and_dropBarrier60a4939d35SEric Fiselier   void arrive_and_drop()  const {
61a4939d35SEric Fiselier     --m_remaining;
62a4939d35SEric Fiselier   }
63a4939d35SEric Fiselier 
wait_for_threadsBarrier64a4939d35SEric Fiselier   void wait_for_threads(int n) const {
65a4939d35SEric Fiselier     while ((m_threads - m_remaining.load()) < n) {
66a4939d35SEric Fiselier       std::this_thread::yield();
67a4939d35SEric Fiselier     }
68a4939d35SEric Fiselier   }
69a4939d35SEric Fiselier 
70a4939d35SEric Fiselier private:
71a4939d35SEric Fiselier   const int m_threads;
72a4939d35SEric Fiselier   mutable std::atomic<int> m_remaining;
73a4939d35SEric Fiselier };
74a4939d35SEric Fiselier 
75a4939d35SEric Fiselier 
7670ebeabfSEric Fiselier enum class InitResult {
7770ebeabfSEric Fiselier   COMPLETE,
7870ebeabfSEric Fiselier   PERFORMED,
7970ebeabfSEric Fiselier   WAITED,
8070ebeabfSEric Fiselier   ABORTED
8170ebeabfSEric Fiselier };
8270ebeabfSEric Fiselier constexpr InitResult COMPLETE = InitResult::COMPLETE;
8370ebeabfSEric Fiselier constexpr InitResult PERFORMED = InitResult::PERFORMED;
8470ebeabfSEric Fiselier constexpr InitResult WAITED = InitResult::WAITED;
8570ebeabfSEric Fiselier constexpr InitResult ABORTED = InitResult::ABORTED;
8670ebeabfSEric Fiselier 
8770ebeabfSEric Fiselier 
8870ebeabfSEric Fiselier template <class Impl, class GuardType, class Init>
check_guard(GuardType * g,Init init)8970ebeabfSEric Fiselier InitResult check_guard(GuardType *g, Init init) {
9070ebeabfSEric Fiselier   uint8_t *first_byte = reinterpret_cast<uint8_t*>(g);
9170ebeabfSEric Fiselier   if (std::__libcpp_atomic_load(first_byte, std::_AO_Acquire) == 0) {
9270ebeabfSEric Fiselier     Impl impl(g);
9370ebeabfSEric Fiselier     if (impl.cxa_guard_acquire() == INIT_IS_PENDING) {
94e6d94f4bSLouis Dionne #ifndef TEST_HAS_NO_EXCEPTIONS
9570ebeabfSEric Fiselier       try {
9670ebeabfSEric Fiselier #endif
9770ebeabfSEric Fiselier         init();
9870ebeabfSEric Fiselier         impl.cxa_guard_release();
9970ebeabfSEric Fiselier         return PERFORMED;
100e6d94f4bSLouis Dionne #ifndef TEST_HAS_NO_EXCEPTIONS
10170ebeabfSEric Fiselier       } catch (...) {
10270ebeabfSEric Fiselier         impl.cxa_guard_abort();
10370ebeabfSEric Fiselier         return ABORTED;
10470ebeabfSEric Fiselier       }
10570ebeabfSEric Fiselier #endif
10670ebeabfSEric Fiselier     }
10770ebeabfSEric Fiselier     return WAITED;
10870ebeabfSEric Fiselier   }
10970ebeabfSEric Fiselier   return COMPLETE;
11070ebeabfSEric Fiselier }
11170ebeabfSEric Fiselier 
11270ebeabfSEric Fiselier 
11370ebeabfSEric Fiselier template <class GuardType, class Impl>
11470ebeabfSEric Fiselier struct FunctionLocalStatic {
FunctionLocalStaticFunctionLocalStatic115a4939d35SEric Fiselier   FunctionLocalStatic() {}
11670ebeabfSEric Fiselier   FunctionLocalStatic(FunctionLocalStatic const&) = delete;
11770ebeabfSEric Fiselier 
11870ebeabfSEric Fiselier   template <class InitFunc>
accessFunctionLocalStatic11970ebeabfSEric Fiselier   InitResult access(InitFunc&& init) {
12070ebeabfSEric Fiselier     auto res = check_guard<Impl>(&guard_object, init);
12170ebeabfSEric Fiselier     ++result_counts[static_cast<int>(res)];
12270ebeabfSEric Fiselier     return res;
12370ebeabfSEric Fiselier   }
12470ebeabfSEric Fiselier 
12570ebeabfSEric Fiselier   template <class InitFn>
126a4939d35SEric Fiselier   struct AccessCallback {
operator ()FunctionLocalStatic::AccessCallback127a4939d35SEric Fiselier     void operator()() const { this_obj->access(init); }
128a4939d35SEric Fiselier 
12970ebeabfSEric Fiselier     FunctionLocalStatic *this_obj;
130a4939d35SEric Fiselier     InitFn init;
13170ebeabfSEric Fiselier   };
13270ebeabfSEric Fiselier 
133a4939d35SEric Fiselier   template <class InitFn, class Callback = AccessCallback< InitFn >  >
access_callbackFunctionLocalStatic134a4939d35SEric Fiselier   Callback access_callback(InitFn init) {
135a4939d35SEric Fiselier     return Callback{this, init};
13670ebeabfSEric Fiselier   }
13770ebeabfSEric Fiselier 
get_countFunctionLocalStatic13870ebeabfSEric Fiselier   int get_count(InitResult I) const {
13970ebeabfSEric Fiselier     return result_counts[static_cast<int>(I)].load();
14070ebeabfSEric Fiselier   }
141a4939d35SEric Fiselier 
num_completedFunctionLocalStatic14270ebeabfSEric Fiselier   int num_completed() const {
14370ebeabfSEric Fiselier     return get_count(COMPLETE) + get_count(PERFORMED) + get_count(WAITED);
14470ebeabfSEric Fiselier   }
145a4939d35SEric Fiselier 
num_waitingFunctionLocalStatic14670ebeabfSEric Fiselier   int num_waiting() const {
14770ebeabfSEric Fiselier     return waiting_threads.load();
14870ebeabfSEric Fiselier   }
14970ebeabfSEric Fiselier 
15070ebeabfSEric Fiselier private:
151a4939d35SEric Fiselier   GuardType guard_object = {};
152a4939d35SEric Fiselier   std::atomic<int> waiting_threads{0};
153a4939d35SEric Fiselier   std::array<std::atomic<int>, 4> result_counts{};
15470ebeabfSEric Fiselier   static_assert(static_cast<int>(ABORTED) == 3, "only 4 result kinds expected");
15570ebeabfSEric Fiselier };
15670ebeabfSEric Fiselier 
15770ebeabfSEric Fiselier struct ThreadGroup {
15870ebeabfSEric Fiselier   ThreadGroup() = default;
15970ebeabfSEric Fiselier   ThreadGroup(ThreadGroup const&) = delete;
16070ebeabfSEric Fiselier 
16170ebeabfSEric Fiselier   template <class ...Args>
CreateThreadGroup16270ebeabfSEric Fiselier   void Create(Args&& ...args) {
16370ebeabfSEric Fiselier     threads.emplace_back(std::forward<Args>(args)...);
16470ebeabfSEric Fiselier   }
16570ebeabfSEric Fiselier 
166a4939d35SEric Fiselier   template <class Callback>
CreateThreadsWithBarrierThreadGroup167a4939d35SEric Fiselier   void CreateThreadsWithBarrier(int N, Callback cb) {
168a4939d35SEric Fiselier     auto start = std::make_shared<Barrier>(N + 1);
169a4939d35SEric Fiselier     for (int I=0; I < N; ++I) {
170a4939d35SEric Fiselier       Create([start, cb]() {
171a4939d35SEric Fiselier         start->arrive_and_wait();
172a4939d35SEric Fiselier         cb();
173a4939d35SEric Fiselier       });
174a4939d35SEric Fiselier     }
175a4939d35SEric Fiselier     start->arrive_and_wait();
176a4939d35SEric Fiselier   }
177a4939d35SEric Fiselier 
JoinAllThreadGroup17870ebeabfSEric Fiselier   void JoinAll() {
17970ebeabfSEric Fiselier     for (auto& t : threads) {
18070ebeabfSEric Fiselier       t.join();
18170ebeabfSEric Fiselier     }
18270ebeabfSEric Fiselier   }
18370ebeabfSEric Fiselier 
18470ebeabfSEric Fiselier private:
18570ebeabfSEric Fiselier   std::vector<std::thread> threads;
18670ebeabfSEric Fiselier };
18770ebeabfSEric Fiselier 
18870ebeabfSEric Fiselier 
18970ebeabfSEric Fiselier template <class GuardType, class Impl>
test_free_for_all(int num_waiters)190a4939d35SEric Fiselier void test_free_for_all(int num_waiters) {
19170ebeabfSEric Fiselier   FunctionLocalStatic<GuardType, Impl> test_obj;
19270ebeabfSEric Fiselier 
19370ebeabfSEric Fiselier   ThreadGroup threads;
194a4939d35SEric Fiselier 
195a4939d35SEric Fiselier   bool already_init = false;
196a4939d35SEric Fiselier   threads.CreateThreadsWithBarrier(num_waiters,
197a4939d35SEric Fiselier     test_obj.access_callback([&]() {
19870ebeabfSEric Fiselier       assert(!already_init);
19970ebeabfSEric Fiselier       already_init = true;
200a4939d35SEric Fiselier     })
20170ebeabfSEric Fiselier   );
20270ebeabfSEric Fiselier 
20370ebeabfSEric Fiselier   // wait for the other threads to finish initialization.
20470ebeabfSEric Fiselier   threads.JoinAll();
20570ebeabfSEric Fiselier 
20670ebeabfSEric Fiselier   assert(test_obj.get_count(PERFORMED) == 1);
207a4939d35SEric Fiselier   assert(test_obj.get_count(COMPLETE) + test_obj.get_count(WAITED) == num_waiters - 1);
208a4939d35SEric Fiselier }
209a4939d35SEric Fiselier 
210a4939d35SEric Fiselier template <class GuardType, class Impl>
test_waiting_for_init(int num_waiters)211a4939d35SEric Fiselier void test_waiting_for_init(int num_waiters) {
212a4939d35SEric Fiselier     FunctionLocalStatic<GuardType, Impl> test_obj;
213a4939d35SEric Fiselier 
214a4939d35SEric Fiselier     ThreadGroup threads;
215a4939d35SEric Fiselier 
216a4939d35SEric Fiselier     Barrier start_init(2);
217a4939d35SEric Fiselier     threads.Create(test_obj.access_callback(
218a4939d35SEric Fiselier       [&]() {
219a4939d35SEric Fiselier         start_init.arrive_and_wait();
220a4939d35SEric Fiselier         // Take our sweet time completing the initialization...
221a4939d35SEric Fiselier         //
222a4939d35SEric Fiselier         // There's a race condition between the other threads reaching the
223a4939d35SEric Fiselier         // start_init barrier, and them actually hitting the cxa guard.
224a4939d35SEric Fiselier         // But we're trying to test the waiting logic, we want as many
225a4939d35SEric Fiselier         // threads to enter the waiting loop as possible.
226a4939d35SEric Fiselier         YieldAfterBarrier();
227a4939d35SEric Fiselier       }
228a4939d35SEric Fiselier     ));
229a4939d35SEric Fiselier     start_init.wait_for_threads(1);
230a4939d35SEric Fiselier 
231a4939d35SEric Fiselier     threads.CreateThreadsWithBarrier(num_waiters,
232a4939d35SEric Fiselier         test_obj.access_callback([]() { assert(false); })
233a4939d35SEric Fiselier     );
234a4939d35SEric Fiselier     // unblock the initializing thread
235a4939d35SEric Fiselier     start_init.arrive_and_drop();
236a4939d35SEric Fiselier 
237a4939d35SEric Fiselier     // wait for the other threads to finish initialization.
238a4939d35SEric Fiselier     threads.JoinAll();
239a4939d35SEric Fiselier 
240a4939d35SEric Fiselier     assert(test_obj.get_count(PERFORMED) == 1);
241a4939d35SEric Fiselier     assert(test_obj.get_count(ABORTED) == 0);
242a4939d35SEric Fiselier     assert(test_obj.get_count(COMPLETE) + test_obj.get_count(WAITED) == num_waiters);
24370ebeabfSEric Fiselier }
24470ebeabfSEric Fiselier 
24570ebeabfSEric Fiselier 
24670ebeabfSEric Fiselier template <class GuardType, class Impl>
test_aborted_init(int num_waiters)247a4939d35SEric Fiselier void test_aborted_init(int num_waiters) {
24870ebeabfSEric Fiselier   FunctionLocalStatic<GuardType, Impl> test_obj;
24970ebeabfSEric Fiselier 
250a4939d35SEric Fiselier   Barrier start_init(2);
25170ebeabfSEric Fiselier   ThreadGroup threads;
252a4939d35SEric Fiselier   threads.Create(test_obj.access_callback(
25370ebeabfSEric Fiselier     [&]() {
254a4939d35SEric Fiselier       start_init.arrive_and_wait();
255a4939d35SEric Fiselier       YieldAfterBarrier();
25670ebeabfSEric Fiselier       throw 42;
257a4939d35SEric Fiselier     })
25870ebeabfSEric Fiselier   );
259a4939d35SEric Fiselier   start_init.wait_for_threads(1);
26070ebeabfSEric Fiselier 
26170ebeabfSEric Fiselier   bool already_init = false;
262a4939d35SEric Fiselier   threads.CreateThreadsWithBarrier(num_waiters,
263a4939d35SEric Fiselier       test_obj.access_callback([&]() {
26470ebeabfSEric Fiselier         assert(!already_init);
26570ebeabfSEric Fiselier         already_init = true;
266a4939d35SEric Fiselier       })
267a4939d35SEric Fiselier     );
26870ebeabfSEric Fiselier   // unblock the initializing thread
269a4939d35SEric Fiselier   start_init.arrive_and_drop();
27070ebeabfSEric Fiselier 
27170ebeabfSEric Fiselier   // wait for the other threads to finish initialization.
27270ebeabfSEric Fiselier   threads.JoinAll();
27370ebeabfSEric Fiselier 
27470ebeabfSEric Fiselier   assert(test_obj.get_count(ABORTED) == 1);
27570ebeabfSEric Fiselier   assert(test_obj.get_count(PERFORMED) == 1);
276a4939d35SEric Fiselier   assert(test_obj.get_count(WAITED) + test_obj.get_count(COMPLETE) == num_waiters - 1);
27770ebeabfSEric Fiselier }
27870ebeabfSEric Fiselier 
27970ebeabfSEric Fiselier 
28070ebeabfSEric Fiselier template <class GuardType, class Impl>
test_completed_init(int num_waiters)281a4939d35SEric Fiselier void test_completed_init(int num_waiters) {
28270ebeabfSEric Fiselier 
28370ebeabfSEric Fiselier   FunctionLocalStatic<GuardType, Impl> test_obj;
28470ebeabfSEric Fiselier 
285a4939d35SEric Fiselier   test_obj.access([]() {}); // initialize the object
28670ebeabfSEric Fiselier   assert(test_obj.num_waiting() == 0);
28770ebeabfSEric Fiselier   assert(test_obj.num_completed() == 1);
28870ebeabfSEric Fiselier   assert(test_obj.get_count(PERFORMED) == 1);
28970ebeabfSEric Fiselier 
29070ebeabfSEric Fiselier   ThreadGroup threads;
291a4939d35SEric Fiselier   threads.CreateThreadsWithBarrier(num_waiters,
292a4939d35SEric Fiselier       test_obj.access_callback([]() { assert(false); })
293a4939d35SEric Fiselier   );
29470ebeabfSEric Fiselier   // wait for the other threads to finish initialization.
29570ebeabfSEric Fiselier   threads.JoinAll();
29670ebeabfSEric Fiselier 
29770ebeabfSEric Fiselier   assert(test_obj.get_count(ABORTED) == 0);
29870ebeabfSEric Fiselier   assert(test_obj.get_count(PERFORMED) == 1);
29970ebeabfSEric Fiselier   assert(test_obj.get_count(WAITED) == 0);
300a4939d35SEric Fiselier   assert(test_obj.get_count(COMPLETE) == num_waiters);
30170ebeabfSEric Fiselier }
30270ebeabfSEric Fiselier 
30370ebeabfSEric Fiselier template <class Impl>
test_impl()30470ebeabfSEric Fiselier void test_impl() {
305a4939d35SEric Fiselier   using TestFn = void(*)(int);
306a4939d35SEric Fiselier   TestFn TestList[] = {
307a4939d35SEric Fiselier     test_free_for_all<uint32_t, Impl>,
308a4939d35SEric Fiselier     test_free_for_all<uint32_t, Impl>,
309a4939d35SEric Fiselier     test_waiting_for_init<uint32_t, Impl>,
310a4939d35SEric Fiselier     test_waiting_for_init<uint64_t, Impl>,
311a4939d35SEric Fiselier     test_aborted_init<uint32_t, Impl>,
312a4939d35SEric Fiselier     test_aborted_init<uint64_t, Impl>,
313a4939d35SEric Fiselier     test_completed_init<uint32_t, Impl>,
314a4939d35SEric Fiselier     test_completed_init<uint64_t, Impl>
315a4939d35SEric Fiselier   };
316a4939d35SEric Fiselier 
317a4939d35SEric Fiselier   for (auto test_func : TestList) {
318a4939d35SEric Fiselier       ThreadGroup test_threads;
319a4939d35SEric Fiselier       test_threads.CreateThreadsWithBarrier(ConcurrentRunsPerTest, [=]() {
320a4939d35SEric Fiselier         for (int I = 0; I < TestSamples; ++I) {
321a4939d35SEric Fiselier           test_func(ThreadsPerTest);
32270ebeabfSEric Fiselier         }
323a4939d35SEric Fiselier       });
324a4939d35SEric Fiselier       test_threads.JoinAll();
32570ebeabfSEric Fiselier     }
32670ebeabfSEric Fiselier   }
32770ebeabfSEric Fiselier 
test_all_impls()3285a235865SEric Fiselier void test_all_impls() {
329e42eeb88SDaniel McIntosh   using MutexImpl = SelectImplementation<Implementation::GlobalMutex>::type;
33070ebeabfSEric Fiselier 
33170ebeabfSEric Fiselier   // Attempt to test the Futex based implementation if it's supported on the
33270ebeabfSEric Fiselier   // target platform.
33370ebeabfSEric Fiselier   using RealFutexImpl = SelectImplementation<Implementation::Futex>::type;
33470ebeabfSEric Fiselier   using FutexImpl = typename std::conditional<
3355a235865SEric Fiselier       PlatformSupportsFutex(),
33670ebeabfSEric Fiselier       RealFutexImpl,
33770ebeabfSEric Fiselier       MutexImpl
33870ebeabfSEric Fiselier   >::type;
33970ebeabfSEric Fiselier 
34070ebeabfSEric Fiselier   test_impl<MutexImpl>();
3415a235865SEric Fiselier   if (PlatformSupportsFutex())
34270ebeabfSEric Fiselier     test_impl<FutexImpl>();
34370ebeabfSEric Fiselier }
3445a235865SEric Fiselier 
3455a235865SEric Fiselier // A dummy
3465a235865SEric Fiselier template <bool Dummy = true>
test_futex_syscall()3475a235865SEric Fiselier void test_futex_syscall() {
3485a235865SEric Fiselier   if (!PlatformSupportsFutex())
3495a235865SEric Fiselier     return;
3505a235865SEric Fiselier   int lock1 = 0;
3515a235865SEric Fiselier   int lock2 = 0;
3525a235865SEric Fiselier   int lock3 = 0;
35356462801SLouis Dionne   std::thread waiter1 = support::make_test_thread([&]() {
3545a235865SEric Fiselier     int expect = 0;
3555a235865SEric Fiselier     PlatformFutexWait(&lock1, expect);
3565a235865SEric Fiselier     assert(lock1 == 1);
3575a235865SEric Fiselier   });
35856462801SLouis Dionne   std::thread waiter2 = support::make_test_thread([&]() {
3595a235865SEric Fiselier     int expect = 0;
3605a235865SEric Fiselier     PlatformFutexWait(&lock2, expect);
3615a235865SEric Fiselier     assert(lock2 == 2);
3625a235865SEric Fiselier   });
36356462801SLouis Dionne   std::thread waiter3 = support::make_test_thread([&]() {
3645a235865SEric Fiselier     int expect = 42; // not the value
3655a235865SEric Fiselier     PlatformFutexWait(&lock3, expect); // doesn't block
3665a235865SEric Fiselier   });
36756462801SLouis Dionne   std::thread waker = support::make_test_thread([&]() {
3685a235865SEric Fiselier     lock1 = 1;
3695a235865SEric Fiselier     PlatformFutexWake(&lock1);
3705a235865SEric Fiselier     lock2 = 2;
3715a235865SEric Fiselier     PlatformFutexWake(&lock2);
3725a235865SEric Fiselier   });
3735a235865SEric Fiselier   waiter1.join();
3745a235865SEric Fiselier   waiter2.join();
3755a235865SEric Fiselier   waiter3.join();
3765a235865SEric Fiselier   waker.join();
3775a235865SEric Fiselier }
3785a235865SEric Fiselier 
main(int,char **)379504bc07dSLouis Dionne int main(int, char**) {
3805a235865SEric Fiselier   // Test each multi-threaded implementation with real threads.
3815a235865SEric Fiselier   test_all_impls();
3825a235865SEric Fiselier   // Test the basic sanity of the futex syscall wrappers.
3835a235865SEric Fiselier   test_futex_syscall();
384504bc07dSLouis Dionne 
385504bc07dSLouis Dionne   return 0;
3865a235865SEric Fiselier }
387