xref: /llvm-project/libcxx/test/support/atomic_helpers.h (revision 886b76128fba5f995c8c8e24aaa2030b59dec01a)
12ff5a56eSwmbat //===----------------------------------------------------------------------===//
22ff5a56eSwmbat //
32ff5a56eSwmbat // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42ff5a56eSwmbat // See https://llvm.org/LICENSE.txt for license information.
52ff5a56eSwmbat // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62ff5a56eSwmbat //
72ff5a56eSwmbat //===----------------------------------------------------------------------===//
82ff5a56eSwmbat 
92ff5a56eSwmbat #ifndef ATOMIC_HELPERS_H
102ff5a56eSwmbat #define ATOMIC_HELPERS_H
112ff5a56eSwmbat 
122ff5a56eSwmbat #include <cassert>
1370617a1aSNikolas Klauser #include <cstdint>
14cc1dfb37SLouis Dionne #include <cstddef>
15cc1dfb37SLouis Dionne #include <type_traits>
162ff5a56eSwmbat 
172ff5a56eSwmbat #include "test_macros.h"
182ff5a56eSwmbat 
19cc1dfb37SLouis Dionne #if defined(TEST_COMPILER_CLANG)
20cc1dfb37SLouis Dionne #  define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE
21cc1dfb37SLouis Dionne #  define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE
22cc1dfb37SLouis Dionne #  define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE
23cc1dfb37SLouis Dionne #  define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE
24cc1dfb37SLouis Dionne #  define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE
25cc1dfb37SLouis Dionne #  define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE
26cc1dfb37SLouis Dionne #elif defined(TEST_COMPILER_GCC)
27cc1dfb37SLouis Dionne #  define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
28cc1dfb37SLouis Dionne #  define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
29cc1dfb37SLouis Dionne #  define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
30cc1dfb37SLouis Dionne #  define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
31cc1dfb37SLouis Dionne #  define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
32cc1dfb37SLouis Dionne #  define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
33*886b7612SStephan T. Lavavej #elif defined(TEST_COMPILER_MSVC)
34cc1dfb37SLouis Dionne // This is lifted from STL/stl/inc/atomic on github for the purposes of
35cc1dfb37SLouis Dionne // keeping the tests compiling for MSVC's STL. It's not a perfect solution
36cc1dfb37SLouis Dionne // but at least the tests will keep running.
37cc1dfb37SLouis Dionne //
38cc1dfb37SLouis Dionne // Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free.
39cc1dfb37SLouis Dionne template <class T, size_t Size = sizeof(T)>
40*886b7612SStephan T. Lavavej constexpr int msvc_is_lock_free_macro_value() {
41*886b7612SStephan T. Lavavej   return (Size <= 8 && (Size & (Size - 1)) == 0) ? 2 : 0;
42cc1dfb37SLouis Dionne }
43cc1dfb37SLouis Dionne #  define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>()
44cc1dfb37SLouis Dionne #  define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>()
45cc1dfb37SLouis Dionne #  define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>()
46cc1dfb37SLouis Dionne #  define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>()
47cc1dfb37SLouis Dionne #  define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>()
48cc1dfb37SLouis Dionne #  define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>()
49cc1dfb37SLouis Dionne #else
50cc1dfb37SLouis Dionne #  error "Unknown compiler"
51cc1dfb37SLouis Dionne #endif
52cc1dfb37SLouis Dionne 
53cc1dfb37SLouis Dionne #ifdef TEST_COMPILER_CLANG
54cc1dfb37SLouis Dionne #  pragma clang diagnostic push
55cc1dfb37SLouis Dionne #  pragma clang diagnostic ignored "-Wc++11-extensions"
56cc1dfb37SLouis Dionne #endif
57cc1dfb37SLouis Dionne 
58cc1dfb37SLouis Dionne enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 };
59cc1dfb37SLouis Dionne 
60cc1dfb37SLouis Dionne // We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required
61cc1dfb37SLouis Dionne // for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type
62cc1dfb37SLouis Dionne // aligned to at least its size is going to be atomic if there exists atomic operations for that size at all,
63cc1dfb37SLouis Dionne // which is true on most platforms. This technically reduces our test coverage in the sense that if a type has
64cc1dfb37SLouis Dionne // an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report
65cc1dfb37SLouis Dionne // that we don't know whether it is lockfree or not.
66cc1dfb37SLouis Dionne #define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T))
67cc1dfb37SLouis Dionne 
68cc1dfb37SLouis Dionne template <class T>
69cc1dfb37SLouis Dionne struct LockFreeStatusInfo {
70cc1dfb37SLouis Dionne   static const LockFreeStatus value = LockFreeStatus(
71cc1dfb37SLouis Dionne       COMPARE_TYPES(T, char)
72cc1dfb37SLouis Dionne           ? TEST_ATOMIC_CHAR_LOCK_FREE
73cc1dfb37SLouis Dionne           : (COMPARE_TYPES(T, short)
74cc1dfb37SLouis Dionne                  ? TEST_ATOMIC_SHORT_LOCK_FREE
75cc1dfb37SLouis Dionne                  : (COMPARE_TYPES(T, int)
76cc1dfb37SLouis Dionne                         ? TEST_ATOMIC_INT_LOCK_FREE
77cc1dfb37SLouis Dionne                         : (COMPARE_TYPES(T, long)
78cc1dfb37SLouis Dionne                                ? TEST_ATOMIC_LONG_LOCK_FREE
79cc1dfb37SLouis Dionne                                : (COMPARE_TYPES(T, long long)
80cc1dfb37SLouis Dionne                                       ? TEST_ATOMIC_LLONG_LOCK_FREE
81cc1dfb37SLouis Dionne                                       : (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1))))));
82cc1dfb37SLouis Dionne 
83cc1dfb37SLouis Dionne   static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown;
84cc1dfb37SLouis Dionne };
85cc1dfb37SLouis Dionne 
86cc1dfb37SLouis Dionne #undef COMPARE_TYPES
87cc1dfb37SLouis Dionne 
88cc1dfb37SLouis Dionne // This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test.
89cc1dfb37SLouis Dionne #if TEST_STD_VER >= 11
90cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<char>::status_known, "");
91cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<short>::status_known, "");
92cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<int>::status_known, "");
93cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<long>::status_known, "");
94cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<void*>::status_known, "");
95cc1dfb37SLouis Dionne 
96cc1dfb37SLouis Dionne // long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is
97cc1dfb37SLouis Dionne // 8 bytes. In that case, atomics may or may not be lockfree based on their address.
98cc1dfb37SLouis Dionne static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, "");
99cc1dfb37SLouis Dionne 
100cc1dfb37SLouis Dionne // Those should always be lock free: hardcode some expected values to make sure our tests are actually
101cc1dfb37SLouis Dionne // testing something meaningful.
102cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, "");
103cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, "");
104cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, "");
105cc1dfb37SLouis Dionne #endif
106cc1dfb37SLouis Dionne 
107cc1dfb37SLouis Dionne // These macros are somewhat suprising to use, since they take the values 0, 1, or 2.
108cc1dfb37SLouis Dionne // To make the tests clearer, get rid of them in preference of LockFreeStatusInfo.
109cc1dfb37SLouis Dionne #undef TEST_ATOMIC_CHAR_LOCK_FREE
110cc1dfb37SLouis Dionne #undef TEST_ATOMIC_SHORT_LOCK_FREE
111cc1dfb37SLouis Dionne #undef TEST_ATOMIC_INT_LOCK_FREE
112cc1dfb37SLouis Dionne #undef TEST_ATOMIC_LONG_LOCK_FREE
113cc1dfb37SLouis Dionne #undef TEST_ATOMIC_LLONG_LOCK_FREE
114cc1dfb37SLouis Dionne #undef TEST_ATOMIC_POINTER_LOCK_FREE
115cc1dfb37SLouis Dionne 
116cc1dfb37SLouis Dionne #ifdef TEST_COMPILER_CLANG
117cc1dfb37SLouis Dionne #  pragma clang diagnostic pop
118cc1dfb37SLouis Dionne #endif
119cc1dfb37SLouis Dionne 
1202ff5a56eSwmbat struct UserAtomicType {
1212ff5a56eSwmbat   int i;
1222ff5a56eSwmbat 
1232ff5a56eSwmbat   explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}
1242ff5a56eSwmbat 
1252ff5a56eSwmbat   friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; }
1262ff5a56eSwmbat };
1272ff5a56eSwmbat 
1282ff5a56eSwmbat /*
1292ff5a56eSwmbat 
1302ff5a56eSwmbat Enable these once we have P0528
1312ff5a56eSwmbat 
1322ff5a56eSwmbat struct WeirdUserAtomicType
1332ff5a56eSwmbat {
1342ff5a56eSwmbat     char i, j, k; // the 3 chars of doom
1352ff5a56eSwmbat 
1362ff5a56eSwmbat     explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}
1372ff5a56eSwmbat 
1382ff5a56eSwmbat     friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y)
1392ff5a56eSwmbat     { return x.i == y.i; }
1402ff5a56eSwmbat };
1412ff5a56eSwmbat 
1422ff5a56eSwmbat struct PaddedUserAtomicType
1432ff5a56eSwmbat {
1442ff5a56eSwmbat     char i; int j; // probably lock-free?
1452ff5a56eSwmbat 
1462ff5a56eSwmbat     explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}
1472ff5a56eSwmbat 
1482ff5a56eSwmbat     friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y)
1492ff5a56eSwmbat     { return x.i == y.i; }
1502ff5a56eSwmbat };
1512ff5a56eSwmbat 
1522ff5a56eSwmbat */
1532ff5a56eSwmbat 
1542ff5a56eSwmbat struct LargeUserAtomicType {
1552ff5a56eSwmbat   int a[128]; /* decidedly not lock-free */
1562ff5a56eSwmbat 
1572ff5a56eSwmbat   LargeUserAtomicType(int d = 0) TEST_NOEXCEPT {
1582ff5a56eSwmbat     for (auto&& e : a)
1592ff5a56eSwmbat       e = d++;
1602ff5a56eSwmbat   }
1612ff5a56eSwmbat 
1622ff5a56eSwmbat   friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT {
1632ff5a56eSwmbat     for (int i = 0; i < 128; ++i)
1642ff5a56eSwmbat       if (x.a[i] != y.a[i])
1652ff5a56eSwmbat         return false;
1662ff5a56eSwmbat     return true;
1672ff5a56eSwmbat   }
1682ff5a56eSwmbat };
1692ff5a56eSwmbat 
1702ff5a56eSwmbat template <template <class TestArg> class TestFunctor>
1712ff5a56eSwmbat struct TestEachIntegralType {
1722ff5a56eSwmbat   void operator()() const {
1732ff5a56eSwmbat     TestFunctor<char>()();
1742ff5a56eSwmbat     TestFunctor<signed char>()();
1752ff5a56eSwmbat     TestFunctor<unsigned char>()();
1762ff5a56eSwmbat     TestFunctor<short>()();
1772ff5a56eSwmbat     TestFunctor<unsigned short>()();
1782ff5a56eSwmbat     TestFunctor<int>()();
1792ff5a56eSwmbat     TestFunctor<unsigned int>()();
1802ff5a56eSwmbat     TestFunctor<long>()();
1812ff5a56eSwmbat     TestFunctor<unsigned long>()();
1822ff5a56eSwmbat     TestFunctor<long long>()();
1832ff5a56eSwmbat     TestFunctor<unsigned long long>()();
1847efe3887SArthur O'Dwyer     TestFunctor<wchar_t>()();
1852ff5a56eSwmbat #if TEST_STD_VER > 17 && defined(__cpp_char8_t)
1862ff5a56eSwmbat     TestFunctor<char8_t>()();
1872ff5a56eSwmbat #endif
1882ff5a56eSwmbat     TestFunctor<char16_t>()();
1892ff5a56eSwmbat     TestFunctor<char32_t>()();
190bd5d0feeSMark de Wever     TestFunctor<std::int8_t>()();
191bd5d0feeSMark de Wever     TestFunctor<std::uint8_t>()();
192bd5d0feeSMark de Wever     TestFunctor<std::int16_t>()();
193bd5d0feeSMark de Wever     TestFunctor<std::uint16_t>()();
194bd5d0feeSMark de Wever     TestFunctor<std::int32_t>()();
195da79d6e1SMark de Wever     TestFunctor<std::uint32_t>()();
196bd5d0feeSMark de Wever     TestFunctor<std::int64_t>()();
197bd5d0feeSMark de Wever     TestFunctor<std::uint64_t>()();
1982ff5a56eSwmbat   }
1992ff5a56eSwmbat };
2002ff5a56eSwmbat 
2012ff5a56eSwmbat template <template <class TestArg> class TestFunctor>
2022ff5a56eSwmbat struct TestEachFloatingPointType {
2032ff5a56eSwmbat   void operator()() const {
2042ff5a56eSwmbat     TestFunctor<float>()();
2052ff5a56eSwmbat     TestFunctor<double>()();
2062ff5a56eSwmbat     TestFunctor<long double>()();
2072ff5a56eSwmbat   }
2082ff5a56eSwmbat };
2092ff5a56eSwmbat 
2102ff5a56eSwmbat template <template <class TestArg> class TestFunctor>
2112ff5a56eSwmbat struct TestEachPointerType {
2122ff5a56eSwmbat   void operator()() const {
2132ff5a56eSwmbat     TestFunctor<int*>()();
2142ff5a56eSwmbat     TestFunctor<const int*>()();
2152ff5a56eSwmbat   }
2162ff5a56eSwmbat };
2172ff5a56eSwmbat 
2182ff5a56eSwmbat template <template <class TestArg> class TestFunctor>
2192ff5a56eSwmbat struct TestEachAtomicType {
2202ff5a56eSwmbat   void operator()() const {
2212ff5a56eSwmbat     TestEachIntegralType<TestFunctor>()();
2229232591bSLouis Dionne     TestEachPointerType<TestFunctor>()();
2232ff5a56eSwmbat     TestFunctor<UserAtomicType>()();
2242ff5a56eSwmbat     /*
2252ff5a56eSwmbat             Note: These aren't going to be lock-free,
22685a847fdSKonstantin Varlamov             so some libatomic.a is necessary.
2272ff5a56eSwmbat         */
2282ff5a56eSwmbat     TestFunctor<LargeUserAtomicType>()();
2292ff5a56eSwmbat     /*
2302ff5a56eSwmbat     Enable these once we have P0528
2312ff5a56eSwmbat 
2322ff5a56eSwmbat         TestFunctor<PaddedUserAtomicType>()();
2332ff5a56eSwmbat         TestFunctor<WeirdUserAtomicType>()();
2342ff5a56eSwmbat */
2352ff5a56eSwmbat     TestFunctor<float>()();
2362ff5a56eSwmbat     TestFunctor<double>()();
2372ff5a56eSwmbat   }
2382ff5a56eSwmbat };
2392ff5a56eSwmbat 
2402ff5a56eSwmbat #endif // ATOMIC_HELPERS_H
241