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