1 //===----------------------------------------------------------------------===//
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 // UNSUPPORTED: c++03
10 
11 // Necessary because we include a private header of libc++abi, which
12 // only understands _LIBCXXABI_HAS_NO_THREADS.
13 #include "test_macros.h"
14 #ifdef TEST_HAS_NO_THREADS
15 # define _LIBCXXABI_HAS_NO_THREADS
16 #endif
17 
18 #define TESTING_CXA_GUARD
19 #include "../src/cxa_guard_impl.h"
20 #include <cassert>
21 
22 #if defined(__clang__)
23 #  pragma clang diagnostic ignored "-Wtautological-pointer-compare"
24 #elif defined(__GNUC__)
25 #  pragma GCC diagnostic ignored "-Waddress"
26 #endif
27 
28 using namespace __cxxabiv1;
29 
30 template <class GuardType, class Impl>
31 struct Tests {
32 private:
33   Tests() : g{}, impl(&g) {}
34   GuardType g;
35   Impl impl;
36 
37   uint8_t first_byte() {
38     uint8_t first;
39     std::memcpy(&first, &g, 1);
40     return first;
41   }
42 
43   void reset() { g = {}; }
44 
45 public:
46   // Test the post conditions on cxa_guard_acquire, cxa_guard_abort, and
47   // cxa_guard_release. Specifically, that they leave the first byte with
48   // the value 0 or 1 as specified by the ARM or Itanium specification.
49   static void test() {
50     Tests tests;
51     tests.test_acquire();
52     tests.test_abort();
53     tests.test_release();
54   }
55 
56   void test_acquire() {
57     {
58       reset();
59       assert(first_byte() == 0);
60       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
61       assert(first_byte() == 0);
62     }
63     {
64       reset();
65       assert(first_byte() == 0);
66       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
67       impl.cxa_guard_release();
68       assert(first_byte() == 1);
69       assert(impl.cxa_guard_acquire() == INIT_IS_DONE);
70     }
71   }
72 
73   void test_release() {
74     {
75       reset();
76       assert(first_byte() == 0);
77       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
78       assert(first_byte() == 0);
79       impl.cxa_guard_release();
80       assert(first_byte() == 1);
81     }
82   }
83 
84   void test_abort() {
85     {
86       reset();
87       assert(first_byte() == 0);
88       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
89       assert(first_byte() == 0);
90       impl.cxa_guard_abort();
91       assert(first_byte() == 0);
92       assert(impl.cxa_guard_acquire() == INIT_IS_PENDING);
93       assert(first_byte() == 0);
94     }
95   }
96 };
97 
98 struct NopMutex {
99   bool lock() {
100     assert(!is_locked);
101     is_locked = true;
102     return false;
103   }
104   bool unlock() {
105     assert(is_locked);
106     is_locked = false;
107     return false;
108   }
109 
110 private:
111   bool is_locked = false;
112 };
113 NopMutex global_nop_mutex = {};
114 
115 struct NopCondVar {
116   bool broadcast() { return false; }
117   bool wait(NopMutex&) { return false; }
118 };
119 NopCondVar global_nop_cond = {};
120 
121 void NopFutexWait(int*, int) { assert(false); }
122 void NopFutexWake(int*) { assert(false); }
123 uint32_t MockGetThreadID() { return 0; }
124 
125 int main(int, char**) {
126   {
127 #if defined(TEST_HAS_NO_THREADS)
128     static_assert(CurrentImplementation == Implementation::NoThreads, "");
129     static_assert(std::is_same<SelectedImplementation, NoThreadsGuard>::value, "");
130 #else
131     static_assert(CurrentImplementation == Implementation::GlobalMutex, "");
132     static_assert(std::is_same<SelectedImplementation,
133                                GlobalMutexGuard<LibcppMutex, LibcppCondVar, GlobalStatic<LibcppMutex>::instance,
134                                                 GlobalStatic<LibcppCondVar>::instance>>::value,
135                   "");
136 #endif
137   }
138   {
139 #if (defined(__APPLE__) || defined(__linux__))  && !defined(TEST_HAS_NO_THREADS)
140     assert(PlatformThreadID);
141 #endif
142     if (PlatformThreadID != nullptr) {
143       assert(PlatformThreadID() != 0);
144       assert(PlatformThreadID() == PlatformThreadID());
145     }
146   }
147   {
148     Tests<uint32_t, NoThreadsGuard>::test();
149     Tests<uint64_t, NoThreadsGuard>::test();
150   }
151   {
152     using MutexImpl = GlobalMutexGuard<NopMutex, NopCondVar, global_nop_mutex, global_nop_cond, MockGetThreadID>;
153     Tests<uint32_t, MutexImpl>::test();
154     Tests<uint64_t, MutexImpl>::test();
155   }
156   {
157     using FutexImpl = FutexGuard<&NopFutexWait, &NopFutexWake, &MockGetThreadID>;
158     Tests<uint32_t, FutexImpl>::test();
159     Tests<uint64_t, FutexImpl>::test();
160   }
161 
162   return 0;
163 }
164