| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef ATOMIC_HELPERS_H |
| #define ATOMIC_HELPERS_H |
| |
| #include <cassert> |
| #include <cstdint> |
| #include <cstddef> |
| #include <type_traits> |
| |
| #include "test_macros.h" |
| |
| #if defined(TEST_COMPILER_CLANG) |
| # define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE |
| # define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE |
| # define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE |
| # define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE |
| # define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE |
| # define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE |
| #elif defined(TEST_COMPILER_GCC) |
| # define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE |
| # define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE |
| # define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE |
| # define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE |
| # define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE |
| # define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE |
| #elif TEST_COMPILER_MSVC |
| // This is lifted from STL/stl/inc/atomic on github for the purposes of |
| // keeping the tests compiling for MSVC's STL. It's not a perfect solution |
| // but at least the tests will keep running. |
| // |
| // Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free. |
| template <class T, size_t Size = sizeof(T)> |
| constexpr bool msvc_is_lock_free_macro_value() { |
| return (Size <= 8 && (Size & Size - 1) == 0) ? 2 : 0; |
| } |
| # define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>() |
| # define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>() |
| # define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>() |
| # define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>() |
| # define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>() |
| # define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>() |
| #else |
| # error "Unknown compiler" |
| #endif |
| |
| #ifdef TEST_COMPILER_CLANG |
| # pragma clang diagnostic push |
| # pragma clang diagnostic ignored "-Wc++11-extensions" |
| #endif |
| |
| enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 }; |
| |
| // We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required |
| // for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type |
| // aligned to at least its size is going to be atomic if there exists atomic operations for that size at all, |
| // which is true on most platforms. This technically reduces our test coverage in the sense that if a type has |
| // an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report |
| // that we don't know whether it is lockfree or not. |
| #define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T)) |
| |
| template <class T> |
| struct LockFreeStatusInfo { |
| static const LockFreeStatus value = LockFreeStatus( |
| COMPARE_TYPES(T, char) |
| ? TEST_ATOMIC_CHAR_LOCK_FREE |
| : (COMPARE_TYPES(T, short) |
| ? TEST_ATOMIC_SHORT_LOCK_FREE |
| : (COMPARE_TYPES(T, int) |
| ? TEST_ATOMIC_INT_LOCK_FREE |
| : (COMPARE_TYPES(T, long) |
| ? TEST_ATOMIC_LONG_LOCK_FREE |
| : (COMPARE_TYPES(T, long long) |
| ? TEST_ATOMIC_LLONG_LOCK_FREE |
| : (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1)))))); |
| |
| static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown; |
| }; |
| |
| #undef COMPARE_TYPES |
| |
| // This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test. |
| #if TEST_STD_VER >= 11 |
| static_assert(LockFreeStatusInfo<char>::status_known, ""); |
| static_assert(LockFreeStatusInfo<short>::status_known, ""); |
| static_assert(LockFreeStatusInfo<int>::status_known, ""); |
| static_assert(LockFreeStatusInfo<long>::status_known, ""); |
| static_assert(LockFreeStatusInfo<void*>::status_known, ""); |
| |
| // long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is |
| // 8 bytes. In that case, atomics may or may not be lockfree based on their address. |
| static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, ""); |
| |
| // Those should always be lock free: hardcode some expected values to make sure our tests are actually |
| // testing something meaningful. |
| static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, ""); |
| static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, ""); |
| static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, ""); |
| #endif |
| |
| // These macros are somewhat suprising to use, since they take the values 0, 1, or 2. |
| // To make the tests clearer, get rid of them in preference of LockFreeStatusInfo. |
| #undef TEST_ATOMIC_CHAR_LOCK_FREE |
| #undef TEST_ATOMIC_SHORT_LOCK_FREE |
| #undef TEST_ATOMIC_INT_LOCK_FREE |
| #undef TEST_ATOMIC_LONG_LOCK_FREE |
| #undef TEST_ATOMIC_LLONG_LOCK_FREE |
| #undef TEST_ATOMIC_POINTER_LOCK_FREE |
| |
| #ifdef TEST_COMPILER_CLANG |
| # pragma clang diagnostic pop |
| #endif |
| |
| struct UserAtomicType { |
| int i; |
| |
| explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} |
| |
| friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; } |
| }; |
| |
| /* |
| |
| Enable these once we have P0528 |
| |
| struct WeirdUserAtomicType |
| { |
| char i, j, k; // the 3 chars of doom |
| |
| explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} |
| |
| friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y) |
| { return x.i == y.i; } |
| }; |
| |
| struct PaddedUserAtomicType |
| { |
| char i; int j; // probably lock-free? |
| |
| explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} |
| |
| friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y) |
| { return x.i == y.i; } |
| }; |
| |
| */ |
| |
| struct LargeUserAtomicType { |
| int a[128]; /* decidedly not lock-free */ |
| |
| LargeUserAtomicType(int d = 0) TEST_NOEXCEPT { |
| for (auto&& e : a) |
| e = d++; |
| } |
| |
| friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT { |
| for (int i = 0; i < 128; ++i) |
| if (x.a[i] != y.a[i]) |
| return false; |
| return true; |
| } |
| }; |
| |
| template <template <class TestArg> class TestFunctor> |
| struct TestEachIntegralType { |
| void operator()() const { |
| TestFunctor<char>()(); |
| TestFunctor<signed char>()(); |
| TestFunctor<unsigned char>()(); |
| TestFunctor<short>()(); |
| TestFunctor<unsigned short>()(); |
| TestFunctor<int>()(); |
| TestFunctor<unsigned int>()(); |
| TestFunctor<long>()(); |
| TestFunctor<unsigned long>()(); |
| TestFunctor<long long>()(); |
| TestFunctor<unsigned long long>()(); |
| TestFunctor<wchar_t>()(); |
| #if TEST_STD_VER > 17 && defined(__cpp_char8_t) |
| TestFunctor<char8_t>()(); |
| #endif |
| TestFunctor<char16_t>()(); |
| TestFunctor<char32_t>()(); |
| TestFunctor<std::int8_t>()(); |
| TestFunctor<std::uint8_t>()(); |
| TestFunctor<std::int16_t>()(); |
| TestFunctor<std::uint16_t>()(); |
| TestFunctor<std::int32_t>()(); |
| TestFunctor<std::uint32_t>()(); |
| TestFunctor<std::int64_t>()(); |
| TestFunctor<std::uint64_t>()(); |
| } |
| }; |
| |
| template <template <class TestArg> class TestFunctor> |
| struct TestEachFloatingPointType { |
| void operator()() const { |
| TestFunctor<float>()(); |
| TestFunctor<double>()(); |
| TestFunctor<long double>()(); |
| } |
| }; |
| |
| template <template <class TestArg> class TestFunctor> |
| struct TestEachPointerType { |
| void operator()() const { |
| TestFunctor<int*>()(); |
| TestFunctor<const int*>()(); |
| } |
| }; |
| |
| template <template <class TestArg> class TestFunctor> |
| struct TestEachAtomicType { |
| void operator()() const { |
| TestEachIntegralType<TestFunctor>()(); |
| TestEachPointerType<TestFunctor>()(); |
| TestFunctor<UserAtomicType>()(); |
| /* |
| Note: These aren't going to be lock-free, |
| so some libatomic.a is necessary. |
| */ |
| TestFunctor<LargeUserAtomicType>()(); |
| /* |
| Enable these once we have P0528 |
| |
| TestFunctor<PaddedUserAtomicType>()(); |
| TestFunctor<WeirdUserAtomicType>()(); |
| */ |
| TestFunctor<float>()(); |
| TestFunctor<double>()(); |
| } |
| }; |
| |
| #endif // ATOMIC_HELPERS_H |