xref: /llvm-project/libcxx/test/support/atomic_helpers.h (revision 886b76128fba5f995c8c8e24aaa2030b59dec01a)
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