Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

My DCAS-class

23 views
Skip to first unread message

Bonita Montero

unread,
Sep 26, 2022, 9:50:31 PM9/26/22
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

Chris M. Thomasson

unread,
Sep 26, 2022, 9:57:14 PM9/26/22
to
On 9/26/2022 6:50 PM, Bonita Montero wrote:
> Hey Chris, this is the current version of my DCAS-class.
[...]

About to cook dinner right now. I will take a look. However, modern C++
on an arch that supports it, should be lock-free. For instance,
cmpxchg8b on 32-bit and cmpxchg128b on 64-bit. However, I remember it
not being that way on certain compilers.

https://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free

returns false for an arch that clearly has DWCAS. DCAS is something
different to me. When I hear DCAS I think two non-contiguous words.
DWCAS is two contiguous words. DW = double word.

Chris M. Thomasson

unread,
Sep 26, 2022, 11:37:15 PM9/26/22
to
On 9/26/2022 6:56 PM, Chris M. Thomasson wrote:
> On 9/26/2022 6:50 PM, Bonita Montero wrote:
>> Hey Chris, this is the current version of my DCAS-class.
> [...]
>
> About to cook dinner right now. I will take a look. However, modern C++
> on an arch that supports it, should be lock-free. For instance,
> cmpxchg8b on 32-bit and cmpxchg128b on 64-bit.

OOPS! I meant, cmpxchg16b. Damn it!

Chris M. Thomasson

unread,
Sep 26, 2022, 11:44:20 PM9/26/22
to
On 9/26/2022 6:50 PM, Bonita Montero wrote:
> Hey Chris, this is the current version of my DCAS-class.
[...]

One usually wants two be able to DWCAS a pointer along with a contiguous
word where the size of a pointer equals the size of a word. Fwiw, I have
some old x86 asm from way back in the day, 16+ years ago that implements
DWCAS on a x86. Gotta love the way back machine... ;^)

Do the links work for you?

https://web.archive.org/web/20070823190955/http://appcore.home.comcast.net/

https://web.archive.org/web/20070505071218/http://appcore.home.comcast.net/ac_src_index.html

in MASM:

https://web.archive.org/web/20060214112539/http://appcore.home.comcast.net/appcore/src/cpu/i686/ac_i686_masm_asm.html

______________________________
align 16
np_ac_i686_atomic_dwcas_fence PROC
push esi
push ebx
mov esi, [esp + 16]
mov eax, [esi]
mov edx, [esi + 4]
mov esi, [esp + 20]
mov ebx, [esi]
mov ecx, [esi + 4]
mov esi, [esp + 12]
lock cmpxchg8b qword ptr [esi]
jne np_ac_i686_atomic_dwcas_fence_fail
xor eax, eax
pop ebx
pop esi
ret

np_ac_i686_atomic_dwcas_fence_fail:
mov esi, [esp + 16]
mov [esi + 0], eax;
mov [esi + 4], edx;
mov eax, 1
pop ebx
pop esi
ret
np_ac_i686_atomic_dwcas_fence ENDP
______________________________


GAS:

https://web.archive.org/web/20060214112345/http://appcore.home.comcast.net/appcore/src/cpu/i686/ac_i686_gcc_asm.html

______________________________
.align 16
.globl np_ac_i686_atomic_dwcas_fence
np_ac_i686_atomic_dwcas_fence:
pushl %esi
pushl %ebx
movl 16(%esp), %esi
movl (%esi), %eax
movl 4(%esi), %edx
movl 20(%esp), %esi
movl (%esi), %ebx
movl 4(%esi), %ecx
movl 12(%esp), %esi
lock cmpxchg8b (%esi)
jne np_ac_i686_atomic_dwcas_fence_fail
xorl %eax, %eax
popl %ebx
popl %esi
ret

np_ac_i686_atomic_dwcas_fence_fail:
movl 16(%esp), %esi
movl %eax, (%esi)
movl %edx, 4(%esi)
movl $1, %eax
popl %ebx
popl %esi
ret
______________________________


Wow, this brings back some memories.

Chris M. Thomasson

unread,
Sep 29, 2022, 3:37:30 PM9/29/22
to
On 9/26/2022 8:43 PM, Chris M. Thomasson wrote:
> On 9/26/2022 6:50 PM, Bonita Montero wrote:
>> Hey Chris, this is the current version of my DCAS-class.
> [...]
>
> One usually wants two be able to DWCAS a pointer along with a contiguous
> word where the size of a pointer equals the size of a word. Fwiw, I have
> some old x86 asm from way back in the day, 16+ years ago that implements
> DWCAS on a x86. Gotta love the way back machine... ;^)
>
> Do the links work for you?
>
> https://web.archive.org/web/20070823190955/http://appcore.home.comcast.net/
[...]
>
> Wow, this brings back some memories.

Talk about abstraction. Check out this old code of mine:

https://web.archive.org/web/20060214112519/http://appcore.home.comcast.net/appcore/include/cpu/i686/ac_i686_h.html

;^)


0 new messages