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 #ifndef ATOMIC_HELPERS_H 10 #define ATOMIC_HELPERS_H 11 12 #include <cassert> 13 #include <cstdint> 14 #include <cstddef> 15 #include <type_traits> 16 17 #include "test_macros.h" 18 19 #if defined(TEST_COMPILER_CLANG) 20 # define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE 21 # define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE 22 # define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE 23 # define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE 24 # define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE 25 # define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE 26 #elif defined(TEST_COMPILER_GCC) 27 # define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE 28 # define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE 29 # define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE 30 # define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE 31 # define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE 32 # define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE 33 #elif defined(TEST_COMPILER_MSVC) 34 // This is lifted from STL/stl/inc/atomic on github for the purposes of 35 // keeping the tests compiling for MSVC's STL. It's not a perfect solution 36 // but at least the tests will keep running. 37 // 38 // Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free. 39 template <class T, size_t Size = sizeof(T)> 40 constexpr int msvc_is_lock_free_macro_value() { 41 return (Size <= 8 && (Size & (Size - 1)) == 0) ? 2 : 0; 42 } 43 # define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>() 44 # define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>() 45 # define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>() 46 # define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>() 47 # define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>() 48 # define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>() 49 #else 50 # error "Unknown compiler" 51 #endif 52 53 #ifdef TEST_COMPILER_CLANG 54 # pragma clang diagnostic push 55 # pragma clang diagnostic ignored "-Wc++11-extensions" 56 #endif 57 58 enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 }; 59 60 // We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required 61 // for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type 62 // aligned to at least its size is going to be atomic if there exists atomic operations for that size at all, 63 // which is true on most platforms. This technically reduces our test coverage in the sense that if a type has 64 // an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report 65 // that we don't know whether it is lockfree or not. 66 #define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T)) 67 68 template <class T> 69 struct LockFreeStatusInfo { 70 static const LockFreeStatus value = LockFreeStatus( 71 COMPARE_TYPES(T, char) 72 ? TEST_ATOMIC_CHAR_LOCK_FREE 73 : (COMPARE_TYPES(T, short) 74 ? TEST_ATOMIC_SHORT_LOCK_FREE 75 : (COMPARE_TYPES(T, int) 76 ? TEST_ATOMIC_INT_LOCK_FREE 77 : (COMPARE_TYPES(T, long) 78 ? TEST_ATOMIC_LONG_LOCK_FREE 79 : (COMPARE_TYPES(T, long long) 80 ? TEST_ATOMIC_LLONG_LOCK_FREE 81 : (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1)))))); 82 83 static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown; 84 }; 85 86 #undef COMPARE_TYPES 87 88 // This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test. 89 #if TEST_STD_VER >= 11 90 static_assert(LockFreeStatusInfo<char>::status_known, ""); 91 static_assert(LockFreeStatusInfo<short>::status_known, ""); 92 static_assert(LockFreeStatusInfo<int>::status_known, ""); 93 static_assert(LockFreeStatusInfo<long>::status_known, ""); 94 static_assert(LockFreeStatusInfo<void*>::status_known, ""); 95 96 // long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is 97 // 8 bytes. In that case, atomics may or may not be lockfree based on their address. 98 static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, ""); 99 100 // Those should always be lock free: hardcode some expected values to make sure our tests are actually 101 // testing something meaningful. 102 static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, ""); 103 static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, ""); 104 static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, ""); 105 #endif 106 107 // These macros are somewhat suprising to use, since they take the values 0, 1, or 2. 108 // To make the tests clearer, get rid of them in preference of LockFreeStatusInfo. 109 #undef TEST_ATOMIC_CHAR_LOCK_FREE 110 #undef TEST_ATOMIC_SHORT_LOCK_FREE 111 #undef TEST_ATOMIC_INT_LOCK_FREE 112 #undef TEST_ATOMIC_LONG_LOCK_FREE 113 #undef TEST_ATOMIC_LLONG_LOCK_FREE 114 #undef TEST_ATOMIC_POINTER_LOCK_FREE 115 116 #ifdef TEST_COMPILER_CLANG 117 # pragma clang diagnostic pop 118 #endif 119 120 struct UserAtomicType { 121 int i; 122 123 explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} 124 125 friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; } 126 }; 127 128 /* 129 130 Enable these once we have P0528 131 132 struct WeirdUserAtomicType 133 { 134 char i, j, k; // the 3 chars of doom 135 136 explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} 137 138 friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y) 139 { return x.i == y.i; } 140 }; 141 142 struct PaddedUserAtomicType 143 { 144 char i; int j; // probably lock-free? 145 146 explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} 147 148 friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y) 149 { return x.i == y.i; } 150 }; 151 152 */ 153 154 struct LargeUserAtomicType { 155 int a[128]; /* decidedly not lock-free */ 156 157 LargeUserAtomicType(int d = 0) TEST_NOEXCEPT { 158 for (auto&& e : a) 159 e = d++; 160 } 161 162 friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT { 163 for (int i = 0; i < 128; ++i) 164 if (x.a[i] != y.a[i]) 165 return false; 166 return true; 167 } 168 }; 169 170 template <template <class TestArg> class TestFunctor> 171 struct TestEachIntegralType { 172 void operator()() const { 173 TestFunctor<char>()(); 174 TestFunctor<signed char>()(); 175 TestFunctor<unsigned char>()(); 176 TestFunctor<short>()(); 177 TestFunctor<unsigned short>()(); 178 TestFunctor<int>()(); 179 TestFunctor<unsigned int>()(); 180 TestFunctor<long>()(); 181 TestFunctor<unsigned long>()(); 182 TestFunctor<long long>()(); 183 TestFunctor<unsigned long long>()(); 184 TestFunctor<wchar_t>()(); 185 #if TEST_STD_VER > 17 && defined(__cpp_char8_t) 186 TestFunctor<char8_t>()(); 187 #endif 188 TestFunctor<char16_t>()(); 189 TestFunctor<char32_t>()(); 190 TestFunctor<std::int8_t>()(); 191 TestFunctor<std::uint8_t>()(); 192 TestFunctor<std::int16_t>()(); 193 TestFunctor<std::uint16_t>()(); 194 TestFunctor<std::int32_t>()(); 195 TestFunctor<std::uint32_t>()(); 196 TestFunctor<std::int64_t>()(); 197 TestFunctor<std::uint64_t>()(); 198 } 199 }; 200 201 template <template <class TestArg> class TestFunctor> 202 struct TestEachFloatingPointType { 203 void operator()() const { 204 TestFunctor<float>()(); 205 TestFunctor<double>()(); 206 TestFunctor<long double>()(); 207 } 208 }; 209 210 template <template <class TestArg> class TestFunctor> 211 struct TestEachPointerType { 212 void operator()() const { 213 TestFunctor<int*>()(); 214 TestFunctor<const int*>()(); 215 } 216 }; 217 218 template <template <class TestArg> class TestFunctor> 219 struct TestEachAtomicType { 220 void operator()() const { 221 TestEachIntegralType<TestFunctor>()(); 222 TestEachPointerType<TestFunctor>()(); 223 TestFunctor<UserAtomicType>()(); 224 /* 225 Note: These aren't going to be lock-free, 226 so some libatomic.a is necessary. 227 */ 228 TestFunctor<LargeUserAtomicType>()(); 229 /* 230 Enable these once we have P0528 231 232 TestFunctor<PaddedUserAtomicType>()(); 233 TestFunctor<WeirdUserAtomicType>()(); 234 */ 235 TestFunctor<float>()(); 236 TestFunctor<double>()(); 237 } 238 }; 239 240 #endif // ATOMIC_HELPERS_H 241