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