atomic.hpp: use SFINAE for waitable atomic detection

Add default_mask<> variable.
Refactor get_value<>().
This commit is contained in:
Nekotekina 2020-12-15 11:19:41 +03:00
parent bfe1a8673a
commit 4a883ba6ea

View File

@ -114,6 +114,36 @@ namespace atomic_wait
constexpr op op_ne = op::eq | op_flag::inverse;
constexpr struct any_value_t
{
template <typename T>
operator T() const noexcept
{
return T();
}
} any_value;
template <typename X, typename T = decltype(std::declval<X>().observe())>
inline __m128i default_mask = sizeof(T) <= 8
? _mm_cvtsi64_si128(UINT64_MAX >> ((64 - sizeof(T) * 8) & 63))
: _mm_set1_epi64x(-1);
template <typename X, typename T = decltype(std::declval<X>().observe())>
constexpr __m128i get_value(X&, T value = T{}, ...)
{
static_assert((sizeof(T) & (sizeof(T) - 1)) == 0);
static_assert(sizeof(T) <= 16);
if constexpr (sizeof(T) <= 8)
{
return _mm_cvtsi64_si128(std::bit_cast<get_uint_t<sizeof(T)>, T>(value));
}
else if constexpr (sizeof(T) == 16)
{
return std::bit_cast<__m128i>(value);
}
}
struct info
{
const void* data;
@ -121,29 +151,13 @@ namespace atomic_wait
__m128i old;
__m128i mask;
template <typename T>
static constexpr __m128i get_value(T value = T{})
template <typename X, typename T = decltype(std::declval<X>().observe())>
constexpr void set_value(X& a, T value = T{})
{
static_assert((sizeof(T) & (sizeof(T) - 1)) == 0);
static_assert(sizeof(T) <= 16);
if constexpr (sizeof(T) <= 8)
{
return _mm_cvtsi64_si128(std::bit_cast<get_uint_t<sizeof(T)>, T>(value));
}
else if constexpr (sizeof(T) == 16)
{
return std::bit_cast<__m128i>(value);
}
old = get_value(a, value);
}
template <typename T>
constexpr void set_value(T value = T{})
{
old = get_value<T>(value);
}
template <typename T>
template <typename X, typename T = decltype(std::declval<X>().observe())>
constexpr void set_mask(T value)
{
static_assert((sizeof(T) & (sizeof(T) - 1)) == 0);
@ -159,23 +173,10 @@ namespace atomic_wait
}
}
template <typename T>
template <typename X, typename T = decltype(std::declval<X>().observe())>
constexpr void set_mask()
{
mask = get_mask<T>();
}
template <typename T>
static constexpr __m128i get_mask()
{
if constexpr (sizeof(T) <= 8)
{
return _mm_cvtsi64_si128(UINT64_MAX >> ((64 - sizeof(T) * 8) & 63));
}
else
{
return _mm_set1_epi64x(-1);
}
mask = default_mask<X>;
}
};
@ -194,9 +195,9 @@ namespace atomic_wait
constexpr list& operator=(const list&) noexcept = default;
template <typename... U, std::size_t... Align>
constexpr list(atomic_t<U, Align>&... vars)
: m_info{{&vars.raw(), sizeof(U), info::get_value<U>(), info::get_mask<U>()}...}
template <typename... U, typename = std::void_t<decltype(std::declval<U>().template wait<op::eq>(any_value))...>>
constexpr list(U&... vars)
: m_info{{&vars, sizeof(vars.observe()), get_value(vars), default_mask<U>}...}
{
static_assert(sizeof...(U) == Max, "Inconsistent amount of atomics.");
}
@ -207,7 +208,7 @@ namespace atomic_wait
static_assert(sizeof...(U) == Max, "Inconsistent amount of values.");
auto* ptr = m_info;
((ptr++)->template set_value<T>(values), ...);
((ptr->template set_value<T>(*static_cast<T*>(ptr->data), values), ptr++), ...);
return *this;
}
@ -221,25 +222,25 @@ namespace atomic_wait
return *this;
}
template <uint Index, op Flags = op::eq, typename T2, std::size_t Align, typename U>
constexpr void set(atomic_t<T2, Align>& var, U value)
template <uint Index, op Flags = op::eq, typename T2, typename U, typename = std::void_t<decltype(std::declval<T2>().template wait<op::eq>(any_value))>>
constexpr void set(T2& var, U value)
{
static_assert(Index < Max);
m_info[Index].data = &var.raw();
m_info[Index].size = sizeof(T2) | (static_cast<u8>(Flags) << 8);
m_info[Index].template set_value<T2>(value);
m_info[Index].data = &var;
m_info[Index].size = sizeof(var.observe()) | (static_cast<u8>(Flags) << 8);
m_info[Index].template set_value<T2>(var, value);
m_info[Index].template set_mask<T2>();
}
template <uint Index, op Flags = op::eq, typename T2, std::size_t Align, typename U, typename V>
constexpr void set(atomic_t<T2, Align>& var, U value, V mask)
template <uint Index, op Flags = op::eq, typename T2, typename U, typename V, typename = std::void_t<decltype(std::declval<T2>().template wait<op::eq>(any_value))>>
constexpr void set(T2& var, U value, V mask)
{
static_assert(Index < Max);
m_info[Index].data = &var.raw();
m_info[Index].size = sizeof(T2) | (static_cast<u8>(Flags) << 8);
m_info[Index].template set_value<T2>(value);
m_info[Index].data = &var;
m_info[Index].size = sizeof(var.observe()) | (static_cast<u8>(Flags) << 8);
m_info[Index].template set_value<T2>(var, value);
m_info[Index].template set_mask<T2>(mask);
}
@ -253,8 +254,8 @@ namespace atomic_wait
}
};
template <typename... T, std::size_t... Align>
list(atomic_t<T, Align>&... vars) -> list<sizeof...(T), T...>;
template <typename... T, typename = std::void_t<decltype(std::declval<T>().template wait<op::eq>(any_value))...>>
list(T&... vars) -> list<sizeof...(T), T...>;
// RDTSC with adjustment for being unique
u64 get_unique_tsc();
@ -1719,3 +1720,9 @@ public:
base::notify_all(1);
}
};
namespace atomic_wait
{
template <std::size_t Align>
inline __m128i default_mask<atomic_t<bool, Align>> = _mm_cvtsi32_si128(1);
}