Bonita Montero
unread,Sep 26, 2022, 9:50:31 PM9/26/22You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to
Hey Chris, this is the current version of my DCAS-class.
#pragma once
#include <cstdint>
#include <utility>
#include <atomic>
#include <type_traits>
#include <bit>
#if defined(_MSC_VER)
#include <intrin.h>
#endif
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 26495)
#endif
struct dcas_atomic
{
static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4, "must be 64
or 32 bit architecture");
using pair_t = std::pair<size_t, size_t>;
dcas_atomic() = default;
dcas_atomic( pair_t const &desired );
operator pair_t();
dcas_atomic &operator =( pair_t const &desired );
size_t get_first();
size_t get_second();
pair_t get_split();
template<bool Release>
bool compare_exchange( pair_t &expected, pair_t const &desired,
std::bool_constant<Release> );
private:
#if defined(__GNUC__) || defined(__clang__)
using dword_t = std::conditional_t<sizeof(size_t) == 8, unsigned
__int128, unsigned long long>;
#endif
static constexpr size_t
FIRST = std::endian::native != std::endian::little,
SECOND = !FIRST;
union alignas(2 * sizeof(size_t))
{
static_assert(sizeof(std::atomic<size_t>) == sizeof(size_t),
"sizeof(atomic<size_t>) must be == sizeof(size_t)");
std::atomic<size_t> m_atomics[2];
#if defined(_MSC_VER)
#if defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM64EC)
__int64 volatile m_firstAndSecond[2];
#elif defined(_M_IX86)
__int64 volatile m_firstAndSecond;
#else
#error unknown architecture
#endif
#elif defined(__GNUC__) || defined(__clang__)
dword_t volatile m_firstAndSecond;
#endif
};
};
inline dcas_atomic::dcas_atomic( pair_t const &desired )
{
m_atomics[FIRST].store( desired.first, std::memory_order_relaxed );
m_atomics[SECOND].store( desired.second, std::memory_order_relaxed );
}
inline dcas_atomic::operator pair_t()
{
using namespace std;
pair_t cmp( get_split() );
compare_exchange( cmp, cmp, false_type() );
return cmp;
}
inline dcas_atomic &dcas_atomic::operator =( pair_t const &desired )
{
pair_t cmp( get_split() );
while( !compare_exchange( cmp, desired, std::false_type() ) );
return *this;
}
inline size_t dcas_atomic::get_first()
{
return m_atomics[FIRST].load( std::memory_order_relaxed );
}
inline size_t dcas_atomic::get_second()
{
return m_atomics[SECOND].load( std::memory_order_relaxed );
}
inline std::pair<size_t, size_t> dcas_atomic::get_split()
{
return pair_t( get_first(), get_second() );
}
template<bool Release>
inline bool dcas_atomic::compare_exchange( pair_t &expected, pair_t
const &desired, std::bool_constant<Release> )
{
using namespace std;
#if defined(_MSC_VER)
#if defined(_M_X64)
return _InterlockedCompareExchange128( m_firstAndSecond,
desired.second, desired.first, (__int64 *)&expected.first );
#elif defined(_M_IX86)
uint64_t
dDesired = desired.first | (uint64_t)desired.second << 32,
dExpected = expected.first | (uint64_t)expected.second << 32,
result = _InterlockedCompareExchange64( &m_firstAndSecond, dDesired,
dExpected );
expected.first = (uint32_t)result;
expected.second = (uint32_t)(result >> 32);
return result == dExpected;
#elif defined(_M_ARM64) || defined(_M_ARM64EC)
if constexpr( Release )
return _InterlockedCompareExchange128_rel( m_firstAndSecond,
desired.second, desired.first, (__int64 *)&expected.first );
else
return _InterlockedCompareExchange128_nf( m_firstAndSecond,
desired.second, desired.first, (__int64 *)&expected.first );
#else
#error unknown architecture
#endif
#elif defined(__GNUC__) || defined(__clang__)
auto compose = []( pair_t const &p ) -> dword_t
{
if constexpr( endian::native == endian::little )
return p.first | (dword_t)p.second << sizeof(size_t) * 8;
else
return p.second | (dword_t)p.first << sizeof(size_t) * 8;
};
dword_t
dExpected = compose( expected ),
dDesired = compose( desired );
bool ret;
if constexpr( Release )
ret = __atomic_compare_exchange_n( &m_firstAndSecond, &dExpected,
dDesired, true, __ATOMIC_RELEASE, __ATOMIC_RELAXED );
else
ret = __atomic_compare_exchange_n( &m_firstAndSecond, &dExpected,
dDesired, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED );
(&expected.first)[FIRST] = (size_t)dExpected;
(&expected.first)[SECOND] = (size_t)(dExpected >> sizeof(size_t) * 8);
return ret;
#else
#error unsupported compiler
#endif
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif