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