From 315c5fb7eb3c7da8a158062190884d52a489747a Mon Sep 17 00:00:00 2001 From: Alexandre Felipe Date: Thu, 8 Oct 2026 18:46:31 +0100 Subject: [PATCH-v1 1/2] half compare exchange. Unfortunately compare exchange doesn't accept a mask, when a 64-bit value is used to store bit field structures in CAS loops, sometimes the compare-exchange operation will fail due to changes in irrelevant bits. e.g. one thread running Several threads running while(!pg_atomic_compare_exchange_u64(ptr, &exp, new)) new = exp | (v & 0x00000000ffffffff}; } and others running while(!pg_atomic_compare_exchange_u64(ptr, &exp, new)) new = exp | (v2 & 0xffffffff00000000); } all threads will conflict with every other, the first operation is linearisable with the second. This commits introduce pg_atomic_compare_exchange_u64_{lo,hi} that allow acting acting on 32-bit halves of a pg_atomic_uint64 separately. --- src/backend/port/atomics.c | 47 +++++++++++++++++++++++++++++ src/include/port/atomics.h | 42 ++++++++++++++++++++++++++ src/include/port/atomics/fallback.h | 4 +++ src/include/port/atomics/generic.h | 46 ++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+) diff --git a/src/backend/port/atomics.c b/src/backend/port/atomics.c index 7a300a5c16d..861a7fe2755 100644 --- a/src/backend/port/atomics.c +++ b/src/backend/port/atomics.c @@ -70,4 +70,51 @@ pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) return oldval; } +bool +pg_atomic_compare_exchange_u64_lo_impl(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval) +{ + bool ret; + u64_halves *cur = (u64_halves *) &ptr->value; + u64_halves *exp = (u64_halves *) expected; + u64_halves *new = (u64_halves *) &newval; + + SpinLockAcquire((slock_t *) &ptr->sema); + + /* perform compare/exchange logic */ + ret = cur->lo == exp->lo; + if (ret) + cur->lo = new->lo; + else + exp->lo = cur->lo; + /* and release lock */ + SpinLockRelease((slock_t *) &ptr->sema); + + return ret; +} + +bool +pg_atomic_compare_exchange_u64_hi_impl(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval) +{ + bool ret; + u64_halves *cur = (u64_halves *) &ptr->value; + u64_halves *exp = (u64_halves *) expected; + u64_halves *new = (u64_halves *) &newval; + + SpinLockAcquire((slock_t *) &ptr->sema); + + /* perform compare/exchange logic */ + ret = cur->hi == exp->hi; + if (ret) + cur->hi = new->hi; + else + exp->hi = cur->hi; + + /* and release lock */ + SpinLockRelease((slock_t *) &ptr->sema); + + return ret; +} + #endif /* PG_HAVE_ATOMIC_U64_SIMULATION */ diff --git a/src/include/port/atomics.h b/src/include/port/atomics.h index a605ea81d07..b7302b28ba1 100644 --- a/src/include/port/atomics.h +++ b/src/include/port/atomics.h @@ -611,6 +611,48 @@ pg_atomic_monotonic_advance_u64(volatile pg_atomic_uint64 *ptr, uint64 target) return currval; } +/* + * Compare and exchange the low half of a pg_atomic_uint64. + * + * Compare low half [31:0] of ptr with the low half of expected. + * If they are equal, change ptr. + * If they are not equal, refresh expected. + * The high half is not touched. + * Return true if the update changed ptr, false if it changed expected. + * + * Full barrier semantics. + */ +static inline bool +pg_atomic_compare_exchange_u64_lo(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval) +{ +#ifndef PG_HAVE_ATOMIC_U64_SIMULATION + AssertPointerAlignment(ptr, 8); +#endif + return pg_atomic_compare_exchange_u64_lo_impl(ptr, expected, newval); +} + +/* + * Compare and exchange the high half of a pg_atomic_uint64. + * + * Compare high half [31:0] of ptr with the high half of expected. + * If they are equal, change ptr. + * If they are not equal, refresh expected. + * The low half is not touched. + * Return true if the update changed ptr, false if it changed expected. + * + * Full barrier semantics. + */ +static inline bool +pg_atomic_compare_exchange_u64_hi(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval) +{ +#ifndef PG_HAVE_ATOMIC_U64_SIMULATION + AssertPointerAlignment(ptr, 8); +#endif + return pg_atomic_compare_exchange_u64_hi_impl(ptr, expected, newval); +} + #undef INSIDE_ATOMICS_H #endif /* ATOMICS_H */ diff --git a/src/include/port/atomics/fallback.h b/src/include/port/atomics/fallback.h index f37225189fc..99eeac0bd91 100644 --- a/src/include/port/atomics/fallback.h +++ b/src/include/port/atomics/fallback.h @@ -35,6 +35,10 @@ extern void pg_atomic_init_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val_) #define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 extern bool pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 *expected, uint64 newval); +extern bool pg_atomic_compare_exchange_u64_lo_impl(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval); +extern bool pg_atomic_compare_exchange_u64_hi_impl(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval); #define PG_HAVE_ATOMIC_FETCH_ADD_U64 extern uint64 pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_); diff --git a/src/include/port/atomics/generic.h b/src/include/port/atomics/generic.h index daa772e9a6d..624ba8177b6 100644 --- a/src/include/port/atomics/generic.h +++ b/src/include/port/atomics/generic.h @@ -428,3 +428,49 @@ pg_atomic_write_membarrier_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val) (void) pg_atomic_exchange_u64_impl(ptr, val); } #endif + +/* + * Plain uint32 halves of a uint64. lo is bits [31:0] and hi is bits [63:32]. + */ +typedef struct u64_halves +{ +#ifndef WORDS_BIGENDIAN + uint32 lo; + uint32 hi; +#else + uint32 hi; + uint32 lo; +#endif +} u64_halves; + +static_assert(sizeof(u64_halves) == sizeof(uint64), + "u64_halves must overlay a uint64"); + +#ifndef PG_HAVE_ATOMIC_U64_SIMULATION + +static_assert(sizeof(pg_atomic_uint32) == sizeof(uint32), + "pg_atomic_uint32 must overlay a uint32"); + +static inline bool +pg_atomic_compare_exchange_u64_lo_impl(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval) +{ + volatile pg_atomic_uint32 *p; + uint32 *exp = &((u64_halves *) expected)->lo; + uint32 new = ((u64_halves *) &newval)->lo; + p = (volatile pg_atomic_uint32 *) &((u64_halves *) &ptr->value)->lo; + return pg_atomic_compare_exchange_u32_impl(p, exp, new); +} + +static inline bool +pg_atomic_compare_exchange_u64_hi_impl(volatile pg_atomic_uint64 *ptr, + uint64 *expected, uint64 newval) +{ + volatile pg_atomic_uint32 *p; + uint32 *exp = &((u64_halves *) expected)->hi; + uint32 new = ((u64_halves *) &newval)->hi; + p = (volatile pg_atomic_uint32 *) &((u64_halves *) &ptr->value)->hi; + return pg_atomic_compare_exchange_u32_impl(p, exp, new); +} + +#endif /* !PG_HAVE_ATOMIC_U64_SIMULATION */ -- 2.53.0