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