//===----------------------------------------------------------------------===// // // 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 #include #include #include #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 defined(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 constexpr int 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() # define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value() # define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value() # define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value() # define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value() # define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value() #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 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::status_known, ""); static_assert(LockFreeStatusInfo::status_known, ""); static_assert(LockFreeStatusInfo::status_known, ""); static_assert(LockFreeStatusInfo::status_known, ""); static_assert(LockFreeStatusInfo::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::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::value == LockFreeStatus::always, ""); static_assert(LockFreeStatusInfo::value == LockFreeStatus::always, ""); static_assert(LockFreeStatusInfo::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