| Deutsch English Français Italiano |
|
<vv3smk$2d66g$1@dont-email.me> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!eternal-september.org!.POSTED!not-for-mail
From: "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Newsgroups: comp.lang.c++
Subject: Re: Futex Stack Test...
Date: Fri, 2 May 2025 18:49:08 -0700
Organization: A noiseless patient Spider
Lines: 231
Message-ID: <vv3smk$2d66g$1@dont-email.me>
References: <vp0g2q$1bu96$1@dont-email.me>
<vv28lf$v2jv$1@raubtier-asyl.eternal-september.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit
Injection-Date: Sat, 03 May 2025 03:49:16 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="3363e58573cc7b7168dab8104afeaf0b";
logging-data="2529488"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX191CPIBsVs8+DbwNvXiiwC+r91yPZtETAM="
User-Agent: Mozilla Thunderbird
Cancel-Lock: sha1:nqgOLKbX4wkViKQIgg8B3+q2ADI=
Content-Language: en-US
In-Reply-To: <vv28lf$v2jv$1@raubtier-asyl.eternal-september.org>
Bytes: 10166
On 5/2/2025 4:01 AM, Wuns Haerst wrote:
> It's not hard to implement a lock-free stack with DW-CAS,
> so no need for futexes. That's my dcas_atomic:
>
> #pragma once
> #include <cstdint>
> #include <utility>
> #include <atomic>
> #include <type_traits>
> #include <climits>
> #include <bit>
> #include <memory>
>
> #if defined(_MSC_VER)
> #pragma warning(push)
> #pragma warning(disable: 26495)
> #endif
> #if defined(__llvm__)
> #pragma clang diagnostic push
> #pragma clang diagnostic ignored "-Watomic-alignment"
> #endif
>
> constexpr int
> DCAS_ACQ_REL = (int)std::memory_order::acq_rel,
> DCAS_ACQUIRE = (int)std::memory_order::acquire,
> DCAS_RELAXED = (int)std::memory_order::relaxed,
> DCAS_RELEASE = (int)std::memory_order::release,
> DCAS_SEQ_CST = (int)std::memory_order::seq_cst;
> template<int Type>
> using dcas_type = std::integral_constant<int, Type>;
> using dcas_acq_rel = dcas_type<DCAS_ACQ_REL>;
> using dcas_acquire = dcas_type<DCAS_ACQUIRE>;
> using dcas_relaxed = dcas_type<DCAS_RELAXED>;
> using dcas_release = dcas_type<DCAS_RELEASE>;
> using dcas_seq_cst = dcas_type<DCAS_SEQ_CST>;
>
> struct dcas_atomic
> {
> static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4, "must be
> 64 or 32 bit architecture");
> struct pair_t { size_t first, second; };
> dcas_atomic() = default;
> dcas_atomic( pair_t const &desired );
> std::atomic<size_t> &first() noexcept;
> std::atomic<size_t> &second() noexcept;
> operator pair_t() noexcept;
> template<int Type>
> bool compare_exchange( pair_t &expected, pair_t const &desired,
> dcas_type<Type> = dcas_seq_cst() ) noexcept;
> template<int Type>
> pair_t load( dcas_type<Type> = dcas_seq_cst() ) const noexcept;
> template<int Type>
> void store( pair_t const &niu, dcas_type<Type> = dcas_seq_cst() )
> noexcept;
> private:
> #if defined(__GNUC__) || defined(__clang__) && !defined(_MSC_VER)
> #if SIZE_WIDTH == 64
> using dword_t = unsigned __int128;
> #elif SIZE_WIDTH == 32
> using dword_t = unsigned long long;
> #else
> #error unknown architecture
> #endif
> #endif
> union alignas(2 * sizeof(size_t)) U
> {
> U() {}
> 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
> } u;
> };
>
> inline dcas_atomic::dcas_atomic( pair_t const &desired )
> {
> u.m_atomics[0].store( desired.first, std::memory_order_relaxed );
> u.m_atomics[1].store( desired.second, std::memory_order_relaxed );
> }
>
> inline std::atomic<size_t> &dcas_atomic::first() noexcept
> {
> return u.m_atomics[0];
> }
>
> inline std::atomic<size_t> &dcas_atomic::second() noexcept
> {
> return u.m_atomics[1];
> }
>
> inline dcas_atomic::operator pair_t() noexcept
> {
> return pair_t( first().load( std::memory_order_relaxed ),
> second().load( std::memory_order_relaxed ) );
> }
>
> template<int Type>
> inline bool dcas_atomic::compare_exchange( pair_t &expected, pair_t
> const &desired, dcas_type<Type> ) noexcept
> {
> using namespace std;
> static_assert(Type == DCAS_ACQ_REL || Type == DCAS_ACQUIRE || Type
> == DCAS_RELAXED || Type == DCAS_RELEASE || Type == DCAS_SEQ_CST);
> #if defined(_MSC_VER)
> #if defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM64EC)
> __int64 expectedA[2];
> expectedA[0] = (__int64)expected.first;
> expectedA[1] = (__int64)expected.second;
> char fRet;
> #if defined(_M_X64)
> fRet = _InterlockedCompareExchange128( u.m_firstAndSecond,
> desired.second, desired.first, expectedA );
> #else
> if constexpr( Type == DCAS_ACQ_REL || Type == DCAS_SEQ_CST )
> fRet = _InterlockedCompareExchange128( u.m_firstAndSecond,
> desired.second, desired.first, expectedA );
> else if constexpr( Type == DCAS_ACQUIRE )
> fRet = _InterlockedCompareExchange128_acq( u.m_firstAndSecond,
> desired.second, desired.first, expectedA );
> else if constexpr( Type == DCAS_RELAXED )
> fRet = _InterlockedCompareExchange128_nf( u.m_firstAndSecond,
> desired.second, desired.first, expectedA );
> else
> fRet = _InterlockedCompareExchange128_rel( u.m_firstAndSecond,
> desired.second, desired.first, expectedA );
> #endif
> if( fRet )
> return true;
> expected.first = expectedA[0];
> expected.second = expectedA[1];
> return false;
> #elif defined(_M_IX86)
> auto compose = []( pair_t const &p ) -> uint64_t { return p.first |
> (uint64_t)p.second << 32; };
> uint64_t
> cDesired = compose( desired ),
> cExpected = compose( expected ),
> cResult = _InterlockedCompareExchange64( &u.m_firstAndSecond,
> cDesired, cExpected );
> if( cResult == cExpected ) [[likely]]
> return true;
> expected.first = (uint32_t)cResult;
> expected.second = (uint32_t)(cResult >> 32);
> return false;
> #else
> #error unspupported Windows-platform
> #endif
> #elif defined(__GNUC__) || defined(__clang__)
> constexpr auto
> pair_t::*FIRST = std::endian::native == std::endian::little ?
> &pair_t::first : &pair_t::second,
> pair_t::*SECOND = std::endian::native == std::endian::little ?
> &pair_t::second : &pair_t::first;
> auto compose = []( pair_t const &p ) -> dword_t { return
> (dword_t)(p.*FIRST) | (dword_t)(p.*SECOND) << SIZE_WIDTH; };
> dword_t
> dwExpected = compose( expected ),
> dwDesired = compose( desired );
> bool ret;
> if constexpr( Type == DCAS_ACQ_REL )
> ret = __atomic_compare_exchange_n( &u.m_firstAndSecond,
> &dwExpected, dwDesired, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED );
> else if constexpr( Type == DCAS_ACQUIRE )
> ret = __atomic_compare_exchange_n( &u.m_firstAndSecond,
> &dwExpected, dwDesired, true, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED );
> else if constexpr( Type == DCAS_RELAXED )
> ret = __atomic_compare_exchange_n( &u.m_firstAndSecond,
> &dwExpected, dwDesired, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED );
========== REMAINDER OF ARTICLE TRUNCATED ==========