241 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef ATOMIC_HELPERS_H10#define ATOMIC_HELPERS_H11 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_FREE21# define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE22# define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE23# define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE24# define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE25# define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE26#elif defined(TEST_COMPILER_GCC)27# define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE28# define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE29# define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE30# define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE31# define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE32# define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE33#elif defined(TEST_COMPILER_MSVC)34// This is lifted from STL/stl/inc/atomic on github for the purposes of35// keeping the tests compiling for MSVC's STL. It's not a perfect solution36// 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.39template <class T, size_t Size = sizeof(T)>40constexpr 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#else50# error "Unknown compiler"51#endif52 53#ifdef TEST_COMPILER_CLANG54# pragma clang diagnostic push55# pragma clang diagnostic ignored "-Wc++11-extensions"56#endif57 58enum 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 required61// for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type62// 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 has64// an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report65// 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 68template <class T>69struct LockFreeStatusInfo {70 static const LockFreeStatus value = LockFreeStatus(71 COMPARE_TYPES(T, char)72 ? TEST_ATOMIC_CHAR_LOCK_FREE73 : (COMPARE_TYPES(T, short)74 ? TEST_ATOMIC_SHORT_LOCK_FREE75 : (COMPARE_TYPES(T, int)76 ? TEST_ATOMIC_INT_LOCK_FREE77 : (COMPARE_TYPES(T, long)78 ? TEST_ATOMIC_LONG_LOCK_FREE79 : (COMPARE_TYPES(T, long long)80 ? TEST_ATOMIC_LLONG_LOCK_FREE81 : (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_TYPES87 88// This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test.89#if TEST_STD_VER >= 1190static_assert(LockFreeStatusInfo<char>::status_known, "");91static_assert(LockFreeStatusInfo<short>::status_known, "");92static_assert(LockFreeStatusInfo<int>::status_known, "");93static_assert(LockFreeStatusInfo<long>::status_known, "");94static_assert(LockFreeStatusInfo<void*>::status_known, "");95 96// long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is97// 8 bytes. In that case, atomics may or may not be lockfree based on their address.98static_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 actually101// testing something meaningful.102static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, "");103static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, "");104static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, "");105#endif106 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_FREE110#undef TEST_ATOMIC_SHORT_LOCK_FREE111#undef TEST_ATOMIC_INT_LOCK_FREE112#undef TEST_ATOMIC_LONG_LOCK_FREE113#undef TEST_ATOMIC_LLONG_LOCK_FREE114#undef TEST_ATOMIC_POINTER_LOCK_FREE115 116#ifdef TEST_COMPILER_CLANG117# pragma clang diagnostic pop118#endif119 120struct 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 130Enable these once we have P0528131 132struct WeirdUserAtomicType133{134 char i, j, k; // the 3 chars of doom135 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 142struct PaddedUserAtomicType143{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 154struct 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 170template <template <class TestArg> class TestFunctor>171struct 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#endif188 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 201template <template <class TestArg> class TestFunctor>202struct TestEachFloatingPointType {203 void operator()() const {204 TestFunctor<float>()();205 TestFunctor<double>()();206 TestFunctor<long double>()();207 }208};209 210template <template <class TestArg> class TestFunctor>211struct TestEachPointerType {212 void operator()() const {213 TestFunctor<int*>()();214 TestFunctor<const int*>()();215 }216};217 218template <template <class TestArg> class TestFunctor>219struct 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 P0528231 232 TestFunctor<PaddedUserAtomicType>()();233 TestFunctor<WeirdUserAtomicType>()();234*/235 TestFunctor<float>()();236 TestFunctor<double>()();237 }238};239 240#endif // ATOMIC_HELPERS_H241