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