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