From f7274f7320a0b1231dd3e676e2b40d2235481189 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Tue, 2 Jul 2024 22:31:42 +1200 Subject: [PATCH v2 3/5] Redefine port/atomics.h on top of . * Redirect types and functions to standard C/C++ * Use instead for C++11 extensions (as formalized by C++23) * Add pg_atomic_uint8, pg_atomic_uint16 and pg_atomic(T) * Provide generic pg_atomic_read(), pg_atomic_write() * Assume that most basic operations will be called by standardized generics in future code, eg atomic_fetch_add() * Forward all the traditional _32, _64 function names for existing code * Reimplement pg_atomic_flag with pg_atomic(uint8) to allow relaxed load (impossible with standard atomic_flag, and pg_atomic(bool) doesn't seem to be portable enough) * Let the compiler/runtime emulate 64 bit atomics with locks for armv7 * The per-compiler headers now just define pg_compiler_barrier_impl() * The per-arch headers now optionally define pg_{memory,read,write}_barrier_impl() but only if they think they can do something better than compilers (to investigate...) * Drop the secondary copy of the ancient spinlock ops from atomics * Drop arch-{arm,ppc}.h, fallback.h and generic.h WIP! TODO: Need to research generated code on all architectures and see if anything important is lost. --- src/include/port/atomics.h | 626 +++++++----------------- src/include/port/atomics/arch-arm.h | 32 -- src/include/port/atomics/arch-ppc.h | 256 ---------- src/include/port/atomics/arch-x86.h | 205 -------- src/include/port/atomics/fallback.h | 42 -- src/include/port/atomics/generic-gcc.h | 307 +----------- src/include/port/atomics/generic-msvc.h | 98 +--- src/include/port/atomics/generic.h | 427 ---------------- src/tools/pgindent/typedefs.list | 2 + 9 files changed, 172 insertions(+), 1823 deletions(-) delete mode 100644 src/include/port/atomics/arch-arm.h delete mode 100644 src/include/port/atomics/arch-ppc.h delete mode 100644 src/include/port/atomics/fallback.h delete mode 100644 src/include/port/atomics/generic.h diff --git a/src/include/port/atomics.h b/src/include/port/atomics.h index 96f1858da97..c4d0e5f5821 100644 --- a/src/include/port/atomics.h +++ b/src/include/port/atomics.h @@ -3,24 +3,33 @@ * atomics.h * Atomic operations. * - * Hardware and compiler dependent functions for manipulating memory - * atomically and dealing with cache coherency. Used to implement locking - * facilities and lockless algorithms/data structures. + * These interfaces are for manipulating memory atomically and dealing with + * cache coherency. They can be used to implement locking facilities and + * lockless algorithms/data structures. * - * To bring up postgres on a platform/compiler at the very least - * implementations for the following operations should be provided: - * * pg_compiler_barrier(), pg_write_barrier(), pg_read_barrier() - * * pg_atomic_compare_exchange_u32(), pg_atomic_fetch_add_u32() - * * pg_atomic_test_set_flag(), pg_atomic_init_flag(), pg_atomic_clear_flag() - * * PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY should be defined if appropriate. + * This is mostly a renaming of , with some differences: * - * There exist generic, hardware independent, implementations for several - * compilers which might be sufficient, although possibly not optimal, for a - * new platform. If no such generic implementation is available spinlocks will - * be used to implement the 64-bit parts of the API. + * * read/write barriers (weaker than acquire/release) + * * they affect even non-atomic access without dependency on atomic access + * * pg_atomic_flag semantics don't allow mapping to atomic_flag * - * Implement _u64 atomics if and only if your platform can use them - * efficiently (and obviously correctly). + * PostgreSQL atomic type width assumptions: + * + * 8, 16: Required to be lock-free (even though implemented with wider + * instructions by the compiler on some RISC-V systems). + * + * 32: Required to be lock-free. No known modern system lacks them. + * + * 64: These can reasonably be expected to be lock-free and fast on + * all modern-ish systems. For one known low-end system they are + * emulated by the compiler/runtime with locks (armv7). + * + * In all cases values must be well aligned or undefined behavior results. + * + * To bring up postgres on a compiler, provide: + * * pg_compiler_barrier() + * Optionally also: + * * pg_memory_barrier(), pg_write_barrier(), pg_read_barrier() * * Use higher level functionality (lwlocks, spinlocks, heavyweight locks) * whenever possible. Writing correct code using these facilities is hard. @@ -38,48 +47,66 @@ #ifndef ATOMICS_H #define ATOMICS_H -#ifdef FRONTEND -#error "atomics.h may not be included from frontend code" +#define INSIDE_ATOMICS_H + +#if defined(__cplusplus) && __cplusplus < 202302L +/* C++11 can't include C's . Use approach from C++23 33.5.12. */ +extern "C++" +{ +#include +#define pg_atomic(T) std::atomic + using std::memory_order_relaxed; + using std::memory_order_acquire; + using std::memory_order_release; + using std::memory_order_acq_rel; + using std::memory_order_seq_cst; +} +#else +#include +#define pg_atomic(T) _Atomic(T) #endif -#define INSIDE_ATOMICS_H +/* + * Visual Studio 2022 doesn't seem to define these in yet, but + * does define them to 2 in . They must hold in C too though, since + * they interoperate. + */ +#if !defined(_MSC_VER) || defined(__cplusplus) +/* Assumptions about lock-free access. */ +StaticAssertDecl(ATOMIC_CHAR_LOCK_FREE >= 2, "need lock-free 8-bit atomics"); +StaticAssertDecl(ATOMIC_SHORT_LOCK_FREE >= 2, "need lock-free 16-bit atomics"); +StaticAssertDecl(ATOMIC_INT_LOCK_FREE >= 2, "need lock-free 32-bit atomics"); +#endif -#include +/* Can't use standard atomic_flag due to extended semantics, see below. */ +typedef pg_atomic(uint8) +pg_atomic_flag; + +/* Common supported atomic integer types. */ +typedef pg_atomic(uint8) +pg_atomic_uint8; +typedef pg_atomic(uint16) +pg_atomic_uint16; +typedef pg_atomic(uint32) +pg_atomic_uint32; +typedef pg_atomic(uint64) +pg_atomic_uint64; /* * First a set of architecture specific files is included. * - * These files can provide the full set of atomics or can do pretty much - * nothing if all the compilers commonly used on these platforms provide - * usable generics. - * - * Don't add an inline assembly of the actual atomic operations if all the - * common implementations of your platform provide intrinsics. Intrinsics are - * much easier to understand and potentially support more architectures. - * * It will often make sense to define memory barrier semantics here, since * e.g. generic compiler intrinsics for x86 memory barriers can't know that * postgres doesn't need x86 read/write barriers do anything more than a * compiler barrier. - * */ -#if defined(__arm__) || defined(__arm) || defined(__aarch64__) -#include "port/atomics/arch-arm.h" -#elif defined(__i386__) || defined(__i386) || defined(__x86_64__) +#if defined(__i386__) || defined(__i386) || defined(__x86_64__) #include "port/atomics/arch-x86.h" -#elif defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) -#include "port/atomics/arch-ppc.h" #endif /* * Compiler specific, but architecture independent implementations. - * - * Provide architecture independent implementations of the atomic - * facilities. At the very least compiler barriers should be provided, but a - * full implementation of - * * pg_compiler_barrier(), pg_write_barrier(), pg_read_barrier() - * * pg_atomic_compare_exchange_u32(), pg_atomic_fetch_add_u32() - * using compiler intrinsics are a good idea. + * This defines compiler barriers. */ /* * gcc or compatible, including clang and icc. @@ -93,29 +120,9 @@ #endif /* Fail if we couldn't find implementations of required facilities. */ -#if !defined(PG_HAVE_ATOMIC_U32_SUPPORT) -#error "could not find an implementation of pg_atomic_uint32" -#endif #if !defined(pg_compiler_barrier_impl) #error "could not find an implementation of pg_compiler_barrier" #endif -#if !defined(pg_memory_barrier_impl) -#error "could not find an implementation of pg_memory_barrier_impl" -#endif - - -/* - * Provide a spinlock-based implementation of the 64 bit variants, if - * necessary. - */ -#include "port/atomics/fallback.h" - -/* - * Provide additional operations using supported infrastructure. These are - * expected to be efficient if the underlying atomic operations are efficient. - */ -#include "port/atomics/generic.h" - /* * pg_compiler_barrier - prevent the compiler from moving code across @@ -125,6 +132,10 @@ * reorder loads or stores to main memory around the barrier. However, the * CPU may still reorder loads or stores at runtime, if the architecture's * memory model permits this. + * + * Unlike standard atomic_signal_fence(), pg_compiler_barrier() prevents + * reordering of non-atomic, non-volatile accesses, so a compiler-specific + * definition is required. */ #define pg_compiler_barrier() pg_compiler_barrier_impl() @@ -137,8 +148,20 @@ * loads and stores are totally ordered (which is not the case on most * architectures) this requires issuing some sort of memory fencing * instruction. + * + * Unlike standard atomic_thread_fence(), pg_{read,write}_barrier() affects + * non-atomic, non-volatile accesses. */ -#define pg_memory_barrier() pg_memory_barrier_impl() +static inline void +pg_memory_barrier(void) +{ +#ifdef pg_memory_barrier_impl + pg_memory_barrier_impl(); +#else + pg_compiler_barrier(); + atomic_thread_fence(memory_order_seq_cst); +#endif +} /* * pg_(read|write)_barrier - prevent the CPU from reordering memory access @@ -150,427 +173,116 @@ * are thus weaker than a full memory barrier, but stronger than a compiler * barrier. In practice, on machines with strong memory ordering, read and * write barriers may require nothing more than a compiler barrier. - */ -#define pg_read_barrier() pg_read_barrier_impl() -#define pg_write_barrier() pg_write_barrier_impl() - -/* - * Spinloop delay - Allow CPU to relax in busy loops - */ -#define pg_spin_delay() pg_spin_delay_impl() - -/* - * pg_atomic_init_flag - initialize atomic flag. * - * No barrier semantics. + * Unlike standard atomic_thread_fence(), pg_{read,write}_barrier() affects + * non-atomic, non-volatile accesses. The default implementation uses + * acquire/release fences, but these are strictly stronger than read/write. */ -static inline void -pg_atomic_init_flag(volatile pg_atomic_flag *ptr) -{ - pg_atomic_init_flag_impl(ptr); -} - -/* - * pg_atomic_test_set_flag - TAS() - * - * Returns true if the flag has successfully been set, false otherwise. - * - * Acquire (including read barrier) semantics. - */ -static inline bool -pg_atomic_test_set_flag(volatile pg_atomic_flag *ptr) -{ - return pg_atomic_test_set_flag_impl(ptr); -} -/* - * pg_atomic_unlocked_test_flag - Check if the lock is free - * - * Returns true if the flag currently is not set, false otherwise. - * - * No barrier semantics. - */ -static inline bool -pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr) -{ - return pg_atomic_unlocked_test_flag_impl(ptr); -} - -/* - * pg_atomic_clear_flag - release lock set by TAS() - * - * Release (including write barrier) semantics. - */ -static inline void -pg_atomic_clear_flag(volatile pg_atomic_flag *ptr) -{ - pg_atomic_clear_flag_impl(ptr); -} - - -/* - * pg_atomic_init_u32 - initialize atomic variable - * - * Has to be done before any concurrent usage.. - * - * No barrier semantics. - */ static inline void -pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val) -{ - AssertPointerAlignment(ptr, 4); - - pg_atomic_init_u32_impl(ptr, val); -} - -/* - * pg_atomic_read_u32 - unlocked read from atomic variable. - * - * The read is guaranteed to return a value as it has been written by this or - * another process at some point in the past. There's however no cache - * coherency interaction guaranteeing the value hasn't since been written to - * again. - * - * No barrier semantics. - */ -static inline uint32 -pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr) +pg_read_barrier(void) { - AssertPointerAlignment(ptr, 4); - return pg_atomic_read_u32_impl(ptr); -} - -/* - * pg_atomic_read_membarrier_u32 - read with barrier semantics. - * - * This read is guaranteed to return the current value, provided that the value - * is only ever updated via operations with barrier semantics, such as - * pg_atomic_compare_exchange_u32() and pg_atomic_write_membarrier_u32(). - * While this may be less performant than pg_atomic_read_u32(), it may be - * easier to reason about correctness with this function in less performance- - * sensitive code. - * - * Full barrier semantics. - */ -static inline uint32 -pg_atomic_read_membarrier_u32(volatile pg_atomic_uint32 *ptr) -{ - AssertPointerAlignment(ptr, 4); - - return pg_atomic_read_membarrier_u32_impl(ptr); -} - -/* - * pg_atomic_write_u32 - write to atomic variable. - * - * The write is guaranteed to succeed as a whole, i.e. it's not possible to - * observe a partial write for any reader. Note that this correctly interacts - * with pg_atomic_compare_exchange_u32, in contrast to - * pg_atomic_unlocked_write_u32(). - * - * No barrier semantics. - */ -static inline void -pg_atomic_write_u32(volatile pg_atomic_uint32 *ptr, uint32 val) -{ - AssertPointerAlignment(ptr, 4); - - pg_atomic_write_u32_impl(ptr, val); -} - -/* - * pg_atomic_unlocked_write_u32 - unlocked write to atomic variable. - * - * The write is guaranteed to succeed as a whole, i.e. it's not possible to - * observe a partial write for any reader. But note that writing this way is - * not guaranteed to correctly interact with read-modify-write operations like - * pg_atomic_compare_exchange_u32. This should only be used in cases where - * minor performance regressions due to atomics emulation are unacceptable. - * - * No barrier semantics. - */ -static inline void -pg_atomic_unlocked_write_u32(volatile pg_atomic_uint32 *ptr, uint32 val) -{ - AssertPointerAlignment(ptr, 4); - - pg_atomic_unlocked_write_u32_impl(ptr, val); +#ifdef pg_read_barrier_impl + pg_read_barrier_impl(); +#else + pg_compiler_barrier(); + atomic_thread_fence(memory_order_acquire); +#endif } -/* - * pg_atomic_write_membarrier_u32 - write with barrier semantics. - * - * The write is guaranteed to succeed as a whole, i.e., it's not possible to - * observe a partial write for any reader. Note that this correctly interacts - * with both pg_atomic_compare_exchange_u32() and - * pg_atomic_read_membarrier_u32(). While this may be less performant than - * pg_atomic_write_u32(), it may be easier to reason about correctness with - * this function in less performance-sensitive code. - * - * Full barrier semantics. - */ static inline void -pg_atomic_write_membarrier_u32(volatile pg_atomic_uint32 *ptr, uint32 val) +pg_write_barrier(void) { - AssertPointerAlignment(ptr, 4); - - pg_atomic_write_membarrier_u32_impl(ptr, val); -} - -/* - * pg_atomic_exchange_u32 - exchange newval with current value - * - * Returns the old value of 'ptr' before the swap. - * - * Full barrier semantics. - */ -static inline uint32 -pg_atomic_exchange_u32(volatile pg_atomic_uint32 *ptr, uint32 newval) -{ - AssertPointerAlignment(ptr, 4); - - return pg_atomic_exchange_u32_impl(ptr, newval); -} - -/* - * pg_atomic_compare_exchange_u32 - CAS operation - * - * Atomically compare the current value of ptr with *expected and store newval - * iff ptr and *expected have the same value. The current value of *ptr will - * always be stored in *expected. - * - * Return true if values have been exchanged, false otherwise. - * - * Full barrier semantics. - */ -static inline bool -pg_atomic_compare_exchange_u32(volatile pg_atomic_uint32 *ptr, - uint32 *expected, uint32 newval) -{ - AssertPointerAlignment(ptr, 4); - AssertPointerAlignment(expected, 4); - - return pg_atomic_compare_exchange_u32_impl(ptr, expected, newval); +#ifdef pg_write_barrier_impl + pg_write_barrier_impl(); +#else + pg_compiler_barrier(); + atomic_thread_fence(memory_order_release); +#endif } /* - * pg_atomic_fetch_add_u32 - atomically add to variable - * - * Returns the value of ptr before the arithmetic operation. + * Operations corresponding to standard atomic_flag. We have to use an integer + * instead of mapping directly to the standard names, to support our relaxed + * check function. * - * Full barrier semantics. + * Acquire and release fences, which are respectively stronger than read and + * write barriers. */ -static inline uint32 -pg_atomic_fetch_add_u32(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - AssertPointerAlignment(ptr, 4); - return pg_atomic_fetch_add_u32_impl(ptr, add_); -} +#define pg_atomic_init_flag(p) atomic_init((p), 1) +#define pg_atomic_test_set_flag(p) \ + atomic_fetch_and_explicit((p), 0, memory_order_acquire) +#define pg_atomic_clear_flag(p) \ + atomic_store_explicit((p), 1, memory_order_release) +#define pg_atomic_unlocked_test_flag(p) \ + atomic_load_explicit((p), memory_order_relaxed) /* - * pg_atomic_fetch_sub_u32 - atomically subtract from variable - * - * Returns the value of ptr before the arithmetic operation. Note that sub_ - * may not be INT_MIN due to platform limitations. - * - * Full barrier semantics. + * Local convention for load/store, relaxed AKA no barrier. These are the only + * "generic" functions provided with pg_ prefixes. It seems pointless to + * rename everything in , but these two are PostgreSQL's + * established way of representing relaxed access, and a lot shorter. */ -static inline uint32 -pg_atomic_fetch_sub_u32(volatile pg_atomic_uint32 *ptr, int32 sub_) -{ - AssertPointerAlignment(ptr, 4); - Assert(sub_ != INT_MIN); - return pg_atomic_fetch_sub_u32_impl(ptr, sub_); -} +#define pg_atomic_read(p) \ + atomic_load_explicit((p), memory_order_relaxed) +#define pg_atomic_write(p, v) \ + atomic_store_explicit((p), (v), memory_order_relaxed) /* - * pg_atomic_fetch_and_u32 - atomically bit-and and_ with variable - * - * Returns the value of ptr before the arithmetic operation. + * Backward-compatible type-specific function names. Only the historical _u32 + * and _64 names are provided. New code and code using other atomic types + * should use the generic functions from the standard directly, and + * pg_atomic_{read,write}() for relaxed access. * - * Full barrier semantics. + * All of these except pg_atomic_(unlocked_){read,write}_{32,64} have seq cst + * AKA full barrier semantics. */ -static inline uint32 -pg_atomic_fetch_and_u32(volatile pg_atomic_uint32 *ptr, uint32 and_) -{ - AssertPointerAlignment(ptr, 4); - return pg_atomic_fetch_and_u32_impl(ptr, and_); -} +#define pg_atomic_init_u32 atomic_init +#define pg_atomic_init_u64 atomic_init +#define pg_atomic_read_u32 pg_atomic_read +#define pg_atomic_read_u64 pg_atomic_read +#define pg_atomic_write_u32 pg_atomic_write +#define pg_atomic_write_u64 pg_atomic_write +#define pg_atomic_unlocked_read_u32 pg_atomic_read +#define pg_atomic_unlocked_read_u64 pg_atomic_read +#define pg_atomic_unlocked_write_u32 pg_atomic_write +#define pg_atomic_unlocked_write_u64 pg_atomic_write +#define pg_atomic_read_membarrier_u32 atomic_load +#define pg_atomic_read_membarrier_u64 atomic_load +#define pg_atomic_write_membarrier_u32 atomic_store +#define pg_atomic_write_membarrier_u64 atomic_store +#define pg_atomic_exchange_u32 atomic_exchange +#define pg_atomic_exchange_u64 atomic_exchange +#define pg_atomic_compare_exchange_u32 atomic_compare_exchange_strong +#define pg_atomic_compare_exchange_u64 atomic_compare_exchange_strong +#define pg_atomic_fetch_add_u32 atomic_fetch_add +#define pg_atomic_fetch_add_u64 atomic_fetch_add +#define pg_atomic_fetch_sub_u32 atomic_fetch_sub +#define pg_atomic_fetch_sub_u64 atomic_fetch_sub +#define pg_atomic_fetch_and_u32 atomic_fetch_and +#define pg_atomic_fetch_and_u64 atomic_fetch_and +#define pg_atomic_fetch_or_u32 atomic_fetch_or +#define pg_atomic_fetch_or_u64 atomic_fetch_or /* - * pg_atomic_fetch_or_u32 - atomically bit-or or_ with variable - * - * Returns the value of ptr before the arithmetic operation. - * - * Full barrier semantics. + * The rest of this file defines non-fundamental convenience functions with no + * counterpart in . */ -static inline uint32 -pg_atomic_fetch_or_u32(volatile pg_atomic_uint32 *ptr, uint32 or_) -{ - AssertPointerAlignment(ptr, 4); - return pg_atomic_fetch_or_u32_impl(ptr, or_); -} /* - * pg_atomic_add_fetch_u32 - atomically add to variable - * - * Returns the value of ptr after the arithmetic operation. - * - * Full barrier semantics. + * Fetch and add/subtract wrappers that return the new value instead of the old + * value. We need functions to avoid double evaluation of v. */ -static inline uint32 -pg_atomic_add_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - AssertPointerAlignment(ptr, 4); - return pg_atomic_add_fetch_u32_impl(ptr, add_); -} - -/* - * pg_atomic_sub_fetch_u32 - atomically subtract from variable - * - * Returns the value of ptr after the arithmetic operation. Note that sub_ may - * not be INT_MIN due to platform limitations. - * - * Full barrier semantics. - */ -static inline uint32 -pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_) -{ - AssertPointerAlignment(ptr, 4); - Assert(sub_ != INT_MIN); - return pg_atomic_sub_fetch_u32_impl(ptr, sub_); -} - -/* ---- - * The 64 bit operations have the same semantics as their 32bit counterparts - * if they are available. Check the corresponding 32bit function for - * documentation. - * ---- - */ -static inline void -pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val) -{ - /* - * Can't necessarily enforce alignment - and don't need it - when using - * the spinlock based fallback implementation. Therefore only assert when - * not using it. - */ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - pg_atomic_init_u64_impl(ptr, val); -} - -static inline uint64 -pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_read_u64_impl(ptr); -} - -static inline uint64 -pg_atomic_read_membarrier_u64(volatile pg_atomic_uint64 *ptr) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_read_membarrier_u64_impl(ptr); -} - -static inline void -pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - pg_atomic_write_u64_impl(ptr, val); -} - -static inline void -pg_atomic_write_membarrier_u64(volatile pg_atomic_uint64 *ptr, uint64 val) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - pg_atomic_write_membarrier_u64_impl(ptr, val); -} - -static inline uint64 -pg_atomic_exchange_u64(volatile pg_atomic_uint64 *ptr, uint64 newval) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_exchange_u64_impl(ptr, newval); -} - -static inline bool -pg_atomic_compare_exchange_u64(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_impl(ptr, expected, newval); -} - -static inline uint64 -pg_atomic_fetch_add_u64(volatile pg_atomic_uint64 *ptr, int64 add_) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_fetch_add_u64_impl(ptr, add_); -} - -static inline uint64 -pg_atomic_fetch_sub_u64(volatile pg_atomic_uint64 *ptr, int64 sub_) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - Assert(sub_ != PG_INT64_MIN); - return pg_atomic_fetch_sub_u64_impl(ptr, sub_); -} - -static inline uint64 -pg_atomic_fetch_and_u64(volatile pg_atomic_uint64 *ptr, uint64 and_) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_fetch_and_u64_impl(ptr, and_); -} - -static inline uint64 -pg_atomic_fetch_or_u64(volatile pg_atomic_uint64 *ptr, uint64 or_) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_fetch_or_u64_impl(ptr, or_); -} - -static inline uint64 -pg_atomic_add_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 add_) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - return pg_atomic_add_fetch_u64_impl(ptr, add_); -} - -static inline uint64 -pg_atomic_sub_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 sub_) -{ -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - Assert(sub_ != PG_INT64_MIN); - return pg_atomic_sub_fetch_u64_impl(ptr, sub_); +#define PG_ATOMIC_GEN_REVERSE_FETCH(size, name, op) \ +static inline uint##size \ +pg_atomic_##name##_fetch_u##size(volatile pg_atomic_uint##size *p, uint##size v) \ +{ \ + return atomic_fetch_##name(p, v) op v; \ } +PG_ATOMIC_GEN_REVERSE_FETCH(32, add, +); +PG_ATOMIC_GEN_REVERSE_FETCH(64, add, +); +PG_ATOMIC_GEN_REVERSE_FETCH(32, sub, -); +PG_ATOMIC_GEN_REVERSE_FETCH(64, sub, -); /* * Monotonically advance the given variable using only atomic operations until @@ -584,11 +296,7 @@ pg_atomic_monotonic_advance_u64(volatile pg_atomic_uint64 *ptr, uint64 target) { uint64 currval; -#ifndef PG_HAVE_ATOMIC_U64_SIMULATION - AssertPointerAlignment(ptr, 8); -#endif - - currval = pg_atomic_read_u64_impl(ptr); + currval = pg_atomic_read_u64(ptr); if (currval >= target) { pg_memory_barrier(); diff --git a/src/include/port/atomics/arch-arm.h b/src/include/port/atomics/arch-arm.h deleted file mode 100644 index eb7e0ac93ba..00000000000 --- a/src/include/port/atomics/arch-arm.h +++ /dev/null @@ -1,32 +0,0 @@ -/*------------------------------------------------------------------------- - * - * arch-arm.h - * Atomic operations considerations specific to ARM - * - * Portions Copyright (c) 2013-2025, PostgreSQL Global Development Group - * - * NOTES: - * - * src/include/port/atomics/arch-arm.h - * - *------------------------------------------------------------------------- - */ - -/* intentionally no include guards, should only be included by atomics.h */ -#ifndef INSIDE_ATOMICS_H -#error "should be included via atomics.h" -#endif - -/* - * 64 bit atomics on ARM32 are implemented using kernel fallbacks and thus - * might be slow, so disable entirely. On ARM64 that problem doesn't exist. - */ -#if !defined(__aarch64__) -#define PG_DISABLE_64_BIT_ATOMICS -#else -/* - * Architecture Reference Manual for ARMv8 states aligned read/write to/from - * general purpose register is atomic. - */ -#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY -#endif /* __aarch64__ */ diff --git a/src/include/port/atomics/arch-ppc.h b/src/include/port/atomics/arch-ppc.h deleted file mode 100644 index b93f6766d29..00000000000 --- a/src/include/port/atomics/arch-ppc.h +++ /dev/null @@ -1,256 +0,0 @@ -/*------------------------------------------------------------------------- - * - * arch-ppc.h - * Atomic operations considerations specific to PowerPC - * - * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * NOTES: - * - * src/include/port/atomics/arch-ppc.h - * - *------------------------------------------------------------------------- - */ - -#if defined(__GNUC__) - -/* - * lwsync orders loads with respect to each other, and similarly with stores. - * But a load can be performed before a subsequent store, so sync must be used - * for a full memory barrier. - */ -#define pg_memory_barrier_impl() __asm__ __volatile__ ("sync" : : : "memory") -#define pg_read_barrier_impl() __asm__ __volatile__ ("lwsync" : : : "memory") -#define pg_write_barrier_impl() __asm__ __volatile__ ("lwsync" : : : "memory") -#endif - -#define PG_HAVE_ATOMIC_U32_SUPPORT -typedef struct pg_atomic_uint32 -{ - volatile uint32 value; -} pg_atomic_uint32; - -/* 64bit atomics are only supported in 64bit mode */ -#if SIZEOF_VOID_P >= 8 -#define PG_HAVE_ATOMIC_U64_SUPPORT -typedef struct pg_atomic_uint64 -{ - volatile uint64 value pg_attribute_aligned(8); -} pg_atomic_uint64; - -#endif - -/* - * This mimics gcc __atomic_compare_exchange_n(..., __ATOMIC_SEQ_CST), but - * code generation differs at the end. __atomic_compare_exchange_n(): - * 100: isync - * 104: mfcr r3 - * 108: rlwinm r3,r3,3,31,31 - * 10c: bne 120 <.eb+0x10> - * 110: clrldi r3,r3,63 - * 114: addi r1,r1,112 - * 118: blr - * 11c: nop - * 120: clrldi r3,r3,63 - * 124: stw r9,0(r4) - * 128: addi r1,r1,112 - * 12c: blr - * - * This: - * f0: isync - * f4: mfcr r9 - * f8: rldicl. r3,r9,35,63 - * fc: bne 104 <.eb> - * 100: stw r10,0(r4) - * 104: addi r1,r1,112 - * 108: blr - * - * This implementation may or may not have materially different performance. - * It's not exploiting the fact that cr0 still holds the relevant comparison - * bits, set during the __asm__. One could fix that by moving more code into - * the __asm__. (That would remove the freedom to eliminate dead stores when - * the caller ignores "expected", but few callers do.) - * - * Recognizing constant "newval" would be superfluous, because there's no - * immediate-operand version of stwcx. - */ -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 -static inline bool -pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, - uint32 *expected, uint32 newval) -{ - uint32 found; - uint32 condition_register; - bool ret; - -#ifdef HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P - if (__builtin_constant_p(*expected) && - (int32) *expected <= PG_INT16_MAX && - (int32) *expected >= PG_INT16_MIN) - __asm__ __volatile__( - " sync \n" - " lwarx %0,0,%5,1 \n" - " cmpwi %0,%3 \n" - " bne $+12 \n" /* branch to lwsync */ - " stwcx. %4,0,%5 \n" - " bne $-16 \n" /* branch to lwarx */ - " lwsync \n" - " mfcr %1 \n" -: "=&r"(found), "=r"(condition_register), "+m"(ptr->value) -: "i"(*expected), "r"(newval), "r"(&ptr->value) -: "memory", "cc"); - else -#endif - __asm__ __volatile__( - " sync \n" - " lwarx %0,0,%5,1 \n" - " cmpw %0,%3 \n" - " bne $+12 \n" /* branch to lwsync */ - " stwcx. %4,0,%5 \n" - " bne $-16 \n" /* branch to lwarx */ - " lwsync \n" - " mfcr %1 \n" -: "=&r"(found), "=r"(condition_register), "+m"(ptr->value) -: "r"(*expected), "r"(newval), "r"(&ptr->value) -: "memory", "cc"); - - ret = (condition_register >> 29) & 1; /* test eq bit of cr0 */ - if (!ret) - *expected = found; - return ret; -} - -/* - * This mirrors gcc __sync_fetch_and_add(). - * - * Like tas(), use constraint "=&b" to avoid allocating r0. - */ -#define PG_HAVE_ATOMIC_FETCH_ADD_U32 -static inline uint32 -pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - uint32 _t; - uint32 res; - -#ifdef HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P - if (__builtin_constant_p(add_) && - add_ <= PG_INT16_MAX && add_ >= PG_INT16_MIN) - __asm__ __volatile__( - " sync \n" - " lwarx %1,0,%4,1 \n" - " addi %0,%1,%3 \n" - " stwcx. %0,0,%4 \n" - " bne $-12 \n" /* branch to lwarx */ - " lwsync \n" -: "=&r"(_t), "=&b"(res), "+m"(ptr->value) -: "i"(add_), "r"(&ptr->value) -: "memory", "cc"); - else -#endif - __asm__ __volatile__( - " sync \n" - " lwarx %1,0,%4,1 \n" - " add %0,%1,%3 \n" - " stwcx. %0,0,%4 \n" - " bne $-12 \n" /* branch to lwarx */ - " lwsync \n" -: "=&r"(_t), "=&r"(res), "+m"(ptr->value) -: "r"(add_), "r"(&ptr->value) -: "memory", "cc"); - - return res; -} - -#ifdef PG_HAVE_ATOMIC_U64_SUPPORT - -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 -static inline bool -pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, - uint64 *expected, uint64 newval) -{ - uint64 found; - uint32 condition_register; - bool ret; - - AssertPointerAlignment(expected, 8); - - /* Like u32, but s/lwarx/ldarx/; s/stwcx/stdcx/; s/cmpw/cmpd/ */ -#ifdef HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P - if (__builtin_constant_p(*expected) && - (int64) *expected <= PG_INT16_MAX && - (int64) *expected >= PG_INT16_MIN) - __asm__ __volatile__( - " sync \n" - " ldarx %0,0,%5,1 \n" - " cmpdi %0,%3 \n" - " bne $+12 \n" /* branch to lwsync */ - " stdcx. %4,0,%5 \n" - " bne $-16 \n" /* branch to ldarx */ - " lwsync \n" - " mfcr %1 \n" -: "=&r"(found), "=r"(condition_register), "+m"(ptr->value) -: "i"(*expected), "r"(newval), "r"(&ptr->value) -: "memory", "cc"); - else -#endif - __asm__ __volatile__( - " sync \n" - " ldarx %0,0,%5,1 \n" - " cmpd %0,%3 \n" - " bne $+12 \n" /* branch to lwsync */ - " stdcx. %4,0,%5 \n" - " bne $-16 \n" /* branch to ldarx */ - " lwsync \n" - " mfcr %1 \n" -: "=&r"(found), "=r"(condition_register), "+m"(ptr->value) -: "r"(*expected), "r"(newval), "r"(&ptr->value) -: "memory", "cc"); - - ret = (condition_register >> 29) & 1; /* test eq bit of cr0 */ - if (!ret) - *expected = found; - return ret; -} - -#define PG_HAVE_ATOMIC_FETCH_ADD_U64 -static inline uint64 -pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) -{ - uint64 _t; - uint64 res; - - /* Like u32, but s/lwarx/ldarx/; s/stwcx/stdcx/ */ -#ifdef HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P - if (__builtin_constant_p(add_) && - add_ <= PG_INT16_MAX && add_ >= PG_INT16_MIN) - __asm__ __volatile__( - " sync \n" - " ldarx %1,0,%4,1 \n" - " addi %0,%1,%3 \n" - " stdcx. %0,0,%4 \n" - " bne $-12 \n" /* branch to ldarx */ - " lwsync \n" -: "=&r"(_t), "=&b"(res), "+m"(ptr->value) -: "i"(add_), "r"(&ptr->value) -: "memory", "cc"); - else -#endif - __asm__ __volatile__( - " sync \n" - " ldarx %1,0,%4,1 \n" - " add %0,%1,%3 \n" - " stdcx. %0,0,%4 \n" - " bne $-12 \n" /* branch to ldarx */ - " lwsync \n" -: "=&r"(_t), "=&r"(res), "+m"(ptr->value) -: "r"(add_), "r"(&ptr->value) -: "memory", "cc"); - - return res; -} - -#endif /* PG_HAVE_ATOMIC_U64_SUPPORT */ - -/* per architecture manual doubleword accesses have single copy atomicity */ -#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY diff --git a/src/include/port/atomics/arch-x86.h b/src/include/port/atomics/arch-x86.h index 4ba2ccc0591..acd612099fa 100644 --- a/src/include/port/atomics/arch-x86.h +++ b/src/include/port/atomics/arch-x86.h @@ -3,10 +3,6 @@ * arch-x86.h * Atomic operations considerations specific to intel x86 * - * Note that we actually require a 486 upwards because the 386 doesn't have - * support for xadd and cmpxchg. Given that the 386 isn't supported anywhere - * anymore that's not much of a restriction luckily. - * * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * @@ -43,204 +39,3 @@ #define pg_read_barrier_impl() pg_compiler_barrier_impl() #define pg_write_barrier_impl() pg_compiler_barrier_impl() - -/* - * Provide implementation for atomics using inline assembly on x86 gcc. It's - * nice to support older gcc's and the compare/exchange implementation here is - * actually more efficient than the * __sync variant. - */ -#if defined(__GNUC__) || defined(__INTEL_COMPILER) - -#define PG_HAVE_ATOMIC_FLAG_SUPPORT -typedef struct pg_atomic_flag -{ - volatile char value; -} pg_atomic_flag; - -#define PG_HAVE_ATOMIC_U32_SUPPORT -typedef struct pg_atomic_uint32 -{ - volatile uint32 value; -} pg_atomic_uint32; - -/* - * It's too complicated to write inline asm for 64bit types on 32bit and the - * 486 can't do it anyway. - */ -#ifdef __x86_64__ -#define PG_HAVE_ATOMIC_U64_SUPPORT -typedef struct pg_atomic_uint64 -{ - /* alignment guaranteed due to being on a 64bit platform */ - volatile uint64 value; -} pg_atomic_uint64; -#endif /* __x86_64__ */ - -#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */ - -#if !defined(PG_HAVE_SPIN_DELAY) -/* - * This sequence is equivalent to the PAUSE instruction ("rep" is - * ignored by old IA32 processors if the following instruction is - * not a string operation); the IA-32 Architecture Software - * Developer's Manual, Vol. 3, Section 7.7.2 describes why using - * PAUSE in the inner loop of a spin lock is necessary for good - * performance: - * - * The PAUSE instruction improves the performance of IA-32 - * processors supporting Hyper-Threading Technology when - * executing spin-wait loops and other routines where one - * thread is accessing a shared lock or semaphore in a tight - * polling loop. When executing a spin-wait loop, the - * processor can suffer a severe performance penalty when - * exiting the loop because it detects a possible memory order - * violation and flushes the core processor's pipeline. The - * PAUSE instruction provides a hint to the processor that the - * code sequence is a spin-wait loop. The processor uses this - * hint to avoid the memory order violation and prevent the - * pipeline flush. In addition, the PAUSE instruction - * de-pipelines the spin-wait loop to prevent it from - * consuming execution resources excessively. - */ -#if defined(__GNUC__) || defined(__INTEL_COMPILER) -#define PG_HAVE_SPIN_DELAY -static __inline__ void -pg_spin_delay_impl(void) -{ - __asm__ __volatile__(" rep; nop \n"); -} -#elif defined(_MSC_VER) && defined(__x86_64__) -#define PG_HAVE_SPIN_DELAY -static __forceinline void -pg_spin_delay_impl(void) -{ - _mm_pause(); -} -#elif defined(_MSC_VER) -#define PG_HAVE_SPIN_DELAY -static __forceinline void -pg_spin_delay_impl(void) -{ - /* See comment for gcc code. Same code, MASM syntax */ - __asm rep nop; -} -#endif -#endif /* !defined(PG_HAVE_SPIN_DELAY) */ - - -#if defined(__GNUC__) || defined(__INTEL_COMPILER) - -#define PG_HAVE_ATOMIC_TEST_SET_FLAG -static inline bool -pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) -{ - char _res = 1; - - __asm__ __volatile__( - " lock \n" - " xchgb %0,%1 \n" -: "+q"(_res), "+m"(ptr->value) -: -: "memory"); - return _res == 0; -} - -#define PG_HAVE_ATOMIC_CLEAR_FLAG -static inline void -pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr) -{ - /* - * On a TSO architecture like x86 it's sufficient to use a compiler - * barrier to achieve release semantics. - */ - __asm__ __volatile__("" ::: "memory"); - ptr->value = 0; -} - -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 -static inline bool -pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, - uint32 *expected, uint32 newval) -{ - char ret; - - /* - * Perform cmpxchg and use the zero flag which it implicitly sets when - * equal to measure the success. - */ - __asm__ __volatile__( - " lock \n" - " cmpxchgl %4,%5 \n" - " setz %2 \n" -: "=a" (*expected), "=m"(ptr->value), "=q" (ret) -: "a" (*expected), "r" (newval), "m"(ptr->value) -: "memory", "cc"); - return (bool) ret; -} - -#define PG_HAVE_ATOMIC_FETCH_ADD_U32 -static inline uint32 -pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - uint32 res; - __asm__ __volatile__( - " lock \n" - " xaddl %0,%1 \n" -: "=q"(res), "=m"(ptr->value) -: "0" (add_), "m"(ptr->value) -: "memory", "cc"); - return res; -} - -#ifdef __x86_64__ - -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 -static inline bool -pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, - uint64 *expected, uint64 newval) -{ - char ret; - - AssertPointerAlignment(expected, 8); - - /* - * Perform cmpxchg and use the zero flag which it implicitly sets when - * equal to measure the success. - */ - __asm__ __volatile__( - " lock \n" - " cmpxchgq %4,%5 \n" - " setz %2 \n" -: "=a" (*expected), "=m"(ptr->value), "=q" (ret) -: "a" (*expected), "r" (newval), "m"(ptr->value) -: "memory", "cc"); - return (bool) ret; -} - -#define PG_HAVE_ATOMIC_FETCH_ADD_U64 -static inline uint64 -pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) -{ - uint64 res; - __asm__ __volatile__( - " lock \n" - " xaddq %0,%1 \n" -: "=q"(res), "=m"(ptr->value) -: "0" (add_), "m"(ptr->value) -: "memory", "cc"); - return res; -} - -#endif /* __x86_64__ */ - -#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */ - -/* - * 8 byte reads / writes have single-copy atomicity on 32 bit x86 platforms - * since at least the 586. As well as on all x86-64 cpus. - */ -#if defined(__i568__) || defined(__i668__) || /* gcc i586+ */ \ - (defined(_M_IX86) && _M_IX86 >= 500) || /* msvc i586+ */ \ - defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) /* gcc, msvc */ -#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY -#endif /* 8 byte single-copy atomicity */ diff --git a/src/include/port/atomics/fallback.h b/src/include/port/atomics/fallback.h deleted file mode 100644 index d2f1b851668..00000000000 --- a/src/include/port/atomics/fallback.h +++ /dev/null @@ -1,42 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fallback.h - * Fallback for platforms without 64 bit atomics support. Slower - * than native atomics support, but not unusably slow. - * - * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * src/include/port/atomics/fallback.h - * - *------------------------------------------------------------------------- - */ - -/* intentionally no include guards, should only be included by atomics.h */ -#ifndef INSIDE_ATOMICS_H -# error "should be included via atomics.h" -#endif - - -#if !defined(PG_HAVE_ATOMIC_U64_SUPPORT) - -#define PG_HAVE_ATOMIC_U64_SIMULATION - -#define PG_HAVE_ATOMIC_U64_SUPPORT -typedef struct pg_atomic_uint64 -{ - int sema; - volatile uint64 value; -} pg_atomic_uint64; - -#define PG_HAVE_ATOMIC_INIT_U64 -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); - -#define PG_HAVE_ATOMIC_FETCH_ADD_U64 -extern uint64 pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_); - -#endif /* PG_HAVE_ATOMIC_U64_SUPPORT */ diff --git a/src/include/port/atomics/generic-gcc.h b/src/include/port/atomics/generic-gcc.h index a0751f2286a..78555a27861 100644 --- a/src/include/port/atomics/generic-gcc.h +++ b/src/include/port/atomics/generic-gcc.h @@ -1,19 +1,11 @@ /*------------------------------------------------------------------------- * * generic-gcc.h - * Atomic operations, implemented using gcc (or compatible) intrinsics. + * Compiler barriers for GCC (or compatible) * * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * NOTES: - * - * Documentation: - * * Legacy __sync Built-in Functions for Atomic Memory Access - * https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fsync-Builtins.html - * * Built-in functions for memory model aware atomic operations - * https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fatomic-Builtins.html - * * src/include/port/atomics/generic-gcc.h * *------------------------------------------------------------------------- @@ -28,300 +20,3 @@ * An empty asm block should be a sufficient compiler barrier. */ #define pg_compiler_barrier_impl() __asm__ __volatile__("" ::: "memory") - -/* - * If we're on GCC, we should be able to get a memory barrier - * out of this compiler built-in. But we prefer to rely on platform specific - * definitions where possible, and use this only as a fallback. - */ -#if !defined(pg_memory_barrier_impl) -# if defined(HAVE_GCC__ATOMIC_INT32_CAS) -# define pg_memory_barrier_impl() __atomic_thread_fence(__ATOMIC_SEQ_CST) -# elif defined(__GNUC__) -# define pg_memory_barrier_impl() __sync_synchronize() -# endif -#endif /* !defined(pg_memory_barrier_impl) */ - -#if !defined(pg_read_barrier_impl) && defined(HAVE_GCC__ATOMIC_INT32_CAS) -/* acquire semantics include read barrier semantics */ -# define pg_read_barrier_impl() do \ -{ \ - pg_compiler_barrier_impl(); \ - __atomic_thread_fence(__ATOMIC_ACQUIRE); \ -} while (0) -#endif - -#if !defined(pg_write_barrier_impl) && defined(HAVE_GCC__ATOMIC_INT32_CAS) -/* release semantics include write barrier semantics */ -# define pg_write_barrier_impl() do \ -{ \ - pg_compiler_barrier_impl(); \ - __atomic_thread_fence(__ATOMIC_RELEASE); \ -} while (0) -#endif - - -/* generic gcc based atomic flag implementation */ -#if !defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) \ - && (defined(HAVE_GCC__SYNC_INT32_TAS) || defined(HAVE_GCC__SYNC_CHAR_TAS)) - -#define PG_HAVE_ATOMIC_FLAG_SUPPORT -typedef struct pg_atomic_flag -{ - /* - * If we have a choice, use int-width TAS, because that is more efficient - * and/or more reliably implemented on most non-Intel platforms. (Note - * that this code isn't used on x86[_64]; see arch-x86.h for that.) - */ -#ifdef HAVE_GCC__SYNC_INT32_TAS - volatile int value; -#else - volatile char value; -#endif -} pg_atomic_flag; - -#endif /* !ATOMIC_FLAG_SUPPORT && SYNC_INT32_TAS */ - -/* generic gcc based atomic uint32 implementation */ -#if !defined(PG_HAVE_ATOMIC_U32_SUPPORT) \ - && (defined(HAVE_GCC__ATOMIC_INT32_CAS) || defined(HAVE_GCC__SYNC_INT32_CAS)) - -#define PG_HAVE_ATOMIC_U32_SUPPORT -typedef struct pg_atomic_uint32 -{ - volatile uint32 value; -} pg_atomic_uint32; - -#endif /* defined(HAVE_GCC__ATOMIC_INT32_CAS) || defined(HAVE_GCC__SYNC_INT32_CAS) */ - -/* generic gcc based atomic uint64 implementation */ -#if !defined(PG_HAVE_ATOMIC_U64_SUPPORT) \ - && !defined(PG_DISABLE_64_BIT_ATOMICS) \ - && (defined(HAVE_GCC__ATOMIC_INT64_CAS) || defined(HAVE_GCC__SYNC_INT64_CAS)) - -#define PG_HAVE_ATOMIC_U64_SUPPORT - -typedef struct pg_atomic_uint64 -{ - volatile uint64 value pg_attribute_aligned(8); -} pg_atomic_uint64; - -#endif /* defined(HAVE_GCC__ATOMIC_INT64_CAS) || defined(HAVE_GCC__SYNC_INT64_CAS) */ - -#ifdef PG_HAVE_ATOMIC_FLAG_SUPPORT - -#if defined(HAVE_GCC__SYNC_CHAR_TAS) || defined(HAVE_GCC__SYNC_INT32_TAS) - -#ifndef PG_HAVE_ATOMIC_TEST_SET_FLAG -#define PG_HAVE_ATOMIC_TEST_SET_FLAG -static inline bool -pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) -{ - /* NB: only an acquire barrier, not a full one */ - /* some platform only support a 1 here */ - return __sync_lock_test_and_set(&ptr->value, 1) == 0; -} -#endif - -#endif /* defined(HAVE_GCC__SYNC_*_TAS) */ - -#ifndef PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG -#define PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG -static inline bool -pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *ptr) -{ - return ptr->value == 0; -} -#endif - -#ifndef PG_HAVE_ATOMIC_CLEAR_FLAG -#define PG_HAVE_ATOMIC_CLEAR_FLAG -static inline void -pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr) -{ - __sync_lock_release(&ptr->value); -} -#endif - -#ifndef PG_HAVE_ATOMIC_INIT_FLAG -#define PG_HAVE_ATOMIC_INIT_FLAG -static inline void -pg_atomic_init_flag_impl(volatile pg_atomic_flag *ptr) -{ - pg_atomic_clear_flag_impl(ptr); -} -#endif - -#endif /* defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) */ - -/* prefer __atomic, it has a better API */ -#if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) && defined(HAVE_GCC__ATOMIC_INT32_CAS) -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 -static inline bool -pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, - uint32 *expected, uint32 newval) -{ - /* FIXME: we can probably use a lower consistency model */ - return __atomic_compare_exchange_n(&ptr->value, expected, newval, false, - __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 -static inline bool -pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, - uint32 *expected, uint32 newval) -{ - bool ret; - uint32 current; - current = __sync_val_compare_and_swap(&ptr->value, *expected, newval); - ret = current == *expected; - *expected = current; - return ret; -} -#endif - -/* - * __sync_lock_test_and_set() only supports setting the value to 1 on some - * platforms, so we only provide an __atomic implementation for - * pg_atomic_exchange. - * - * We assume the availability of 32-bit __atomic_compare_exchange_n() implies - * the availability of 32-bit __atomic_exchange_n(). - */ -#if !defined(PG_HAVE_ATOMIC_EXCHANGE_U32) && defined(HAVE_GCC__ATOMIC_INT32_CAS) -#define PG_HAVE_ATOMIC_EXCHANGE_U32 -static inline uint32 -pg_atomic_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 newval) -{ - return __atomic_exchange_n(&ptr->value, newval, __ATOMIC_SEQ_CST); -} -#endif - -/* if we have 32-bit __sync_val_compare_and_swap, assume we have these too: */ - -#if !defined(PG_HAVE_ATOMIC_FETCH_ADD_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) -#define PG_HAVE_ATOMIC_FETCH_ADD_U32 -static inline uint32 -pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - return __sync_fetch_and_add(&ptr->value, add_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_SUB_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) -#define PG_HAVE_ATOMIC_FETCH_SUB_U32 -static inline uint32 -pg_atomic_fetch_sub_u32_impl(volatile pg_atomic_uint32 *ptr, int32 sub_) -{ - return __sync_fetch_and_sub(&ptr->value, sub_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_AND_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) -#define PG_HAVE_ATOMIC_FETCH_AND_U32 -static inline uint32 -pg_atomic_fetch_and_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 and_) -{ - return __sync_fetch_and_and(&ptr->value, and_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_OR_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) -#define PG_HAVE_ATOMIC_FETCH_OR_U32 -static inline uint32 -pg_atomic_fetch_or_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 or_) -{ - return __sync_fetch_and_or(&ptr->value, or_); -} -#endif - - -#if !defined(PG_DISABLE_64_BIT_ATOMICS) - -#if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) && defined(HAVE_GCC__ATOMIC_INT64_CAS) -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 -static inline bool -pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, - uint64 *expected, uint64 newval) -{ - AssertPointerAlignment(expected, 8); - return __atomic_compare_exchange_n(&ptr->value, expected, newval, false, - __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 -static inline bool -pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, - uint64 *expected, uint64 newval) -{ - bool ret; - uint64 current; - - AssertPointerAlignment(expected, 8); - current = __sync_val_compare_and_swap(&ptr->value, *expected, newval); - ret = current == *expected; - *expected = current; - return ret; -} -#endif - -/* - * __sync_lock_test_and_set() only supports setting the value to 1 on some - * platforms, so we only provide an __atomic implementation for - * pg_atomic_exchange. - * - * We assume the availability of 64-bit __atomic_compare_exchange_n() implies - * the availability of 64-bit __atomic_exchange_n(). - */ -#if !defined(PG_HAVE_ATOMIC_EXCHANGE_U64) && defined(HAVE_GCC__ATOMIC_INT64_CAS) -#define PG_HAVE_ATOMIC_EXCHANGE_U64 -static inline uint64 -pg_atomic_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 newval) -{ - return __atomic_exchange_n(&ptr->value, newval, __ATOMIC_SEQ_CST); -} -#endif - -/* if we have 64-bit __sync_val_compare_and_swap, assume we have these too: */ - -#if !defined(PG_HAVE_ATOMIC_FETCH_ADD_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) -#define PG_HAVE_ATOMIC_FETCH_ADD_U64 -static inline uint64 -pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) -{ - return __sync_fetch_and_add(&ptr->value, add_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_SUB_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) -#define PG_HAVE_ATOMIC_FETCH_SUB_U64 -static inline uint64 -pg_atomic_fetch_sub_u64_impl(volatile pg_atomic_uint64 *ptr, int64 sub_) -{ - return __sync_fetch_and_sub(&ptr->value, sub_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_AND_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) -#define PG_HAVE_ATOMIC_FETCH_AND_U64 -static inline uint64 -pg_atomic_fetch_and_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 and_) -{ - return __sync_fetch_and_and(&ptr->value, and_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_OR_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) -#define PG_HAVE_ATOMIC_FETCH_OR_U64 -static inline uint64 -pg_atomic_fetch_or_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 or_) -{ - return __sync_fetch_and_or(&ptr->value, or_); -} -#endif - -#endif /* !defined(PG_DISABLE_64_BIT_ATOMICS) */ diff --git a/src/include/port/atomics/generic-msvc.h b/src/include/port/atomics/generic-msvc.h index a6ea5f1c2e7..4cb5eab5be6 100644 --- a/src/include/port/atomics/generic-msvc.h +++ b/src/include/port/atomics/generic-msvc.h @@ -1,17 +1,11 @@ /*------------------------------------------------------------------------- * * generic-msvc.h - * Atomic operations support when using MSVC + * Compiler barriers when using MSVC * * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * NOTES: - * - * Documentation: - * * Interlocked Variable Access - * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx - * * src/include/port/atomics/generic-msvc.h * *------------------------------------------------------------------------- @@ -24,92 +18,4 @@ #endif #pragma intrinsic(_ReadWriteBarrier) -#define pg_compiler_barrier_impl() _ReadWriteBarrier() - -#ifndef pg_memory_barrier_impl -#define pg_memory_barrier_impl() MemoryBarrier() -#endif - -#define PG_HAVE_ATOMIC_U32_SUPPORT -typedef struct pg_atomic_uint32 -{ - volatile uint32 value; -} pg_atomic_uint32; - -#define PG_HAVE_ATOMIC_U64_SUPPORT -typedef struct pg_attribute_aligned(8) pg_atomic_uint64 -{ - volatile uint64 value; -} pg_atomic_uint64; - - -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 -static inline bool -pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, - uint32 *expected, uint32 newval) -{ - bool ret; - uint32 current; - current = InterlockedCompareExchange(&ptr->value, newval, *expected); - ret = current == *expected; - *expected = current; - return ret; -} - -#define PG_HAVE_ATOMIC_EXCHANGE_U32 -static inline uint32 -pg_atomic_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 newval) -{ - return InterlockedExchange(&ptr->value, newval); -} - -#define PG_HAVE_ATOMIC_FETCH_ADD_U32 -static inline uint32 -pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - return InterlockedExchangeAdd(&ptr->value, add_); -} - -/* - * The non-intrinsics versions are only available in vista upwards, so use the - * intrinsic version. Only supported on >486, but we require XP as a minimum - * baseline, which doesn't support the 486, so we don't need to add checks for - * that case. - */ -#pragma intrinsic(_InterlockedCompareExchange64) - -#define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 -static inline bool -pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, - uint64 *expected, uint64 newval) -{ - bool ret; - uint64 current; - current = _InterlockedCompareExchange64(&ptr->value, newval, *expected); - ret = current == *expected; - *expected = current; - return ret; -} - -/* Only implemented on 64bit builds */ -#ifdef _WIN64 - -#pragma intrinsic(_InterlockedExchange64) - -#define PG_HAVE_ATOMIC_EXCHANGE_U64 -static inline uint64 -pg_atomic_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 newval) -{ - return _InterlockedExchange64(&ptr->value, newval); -} - -#pragma intrinsic(_InterlockedExchangeAdd64) - -#define PG_HAVE_ATOMIC_FETCH_ADD_U64 -static inline uint64 -pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) -{ - return _InterlockedExchangeAdd64(&ptr->value, add_); -} - -#endif /* _WIN64 */ +#define pg_compiler_barrier_impl() _ReadWriteBarrier() diff --git a/src/include/port/atomics/generic.h b/src/include/port/atomics/generic.h deleted file mode 100644 index 6b61a7b5416..00000000000 --- a/src/include/port/atomics/generic.h +++ /dev/null @@ -1,427 +0,0 @@ -/*------------------------------------------------------------------------- - * - * generic.h - * Implement higher level operations based on some lower level atomic - * operations. - * - * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * src/include/port/atomics/generic.h - * - *------------------------------------------------------------------------- - */ - -/* intentionally no include guards, should only be included by atomics.h */ -#ifndef INSIDE_ATOMICS_H -# error "should be included via atomics.h" -#endif - -/* - * If read or write barriers are undefined, we upgrade them to full memory - * barriers. - */ -#if !defined(pg_read_barrier_impl) -# define pg_read_barrier_impl pg_memory_barrier_impl -#endif -#if !defined(pg_write_barrier_impl) -# define pg_write_barrier_impl pg_memory_barrier_impl -#endif - -#ifndef PG_HAVE_SPIN_DELAY -#define PG_HAVE_SPIN_DELAY -#define pg_spin_delay_impl() ((void)0) -#endif - - -/* provide fallback */ -#if !defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) && defined(PG_HAVE_ATOMIC_U32_SUPPORT) -#define PG_HAVE_ATOMIC_FLAG_SUPPORT -typedef pg_atomic_uint32 pg_atomic_flag; -#endif - -#ifndef PG_HAVE_ATOMIC_READ_U32 -#define PG_HAVE_ATOMIC_READ_U32 -static inline uint32 -pg_atomic_read_u32_impl(volatile pg_atomic_uint32 *ptr) -{ - return ptr->value; -} -#endif - -#ifndef PG_HAVE_ATOMIC_WRITE_U32 -#define PG_HAVE_ATOMIC_WRITE_U32 -static inline void -pg_atomic_write_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val) -{ - ptr->value = val; -} -#endif - -#ifndef PG_HAVE_ATOMIC_UNLOCKED_WRITE_U32 -#define PG_HAVE_ATOMIC_UNLOCKED_WRITE_U32 -static inline void -pg_atomic_unlocked_write_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val) -{ - ptr->value = val; -} -#endif - -/* - * provide fallback for test_and_set using atomic_exchange if available - */ -#if !defined(PG_HAVE_ATOMIC_TEST_SET_FLAG) && defined(PG_HAVE_ATOMIC_EXCHANGE_U32) - -#define PG_HAVE_ATOMIC_INIT_FLAG -static inline void -pg_atomic_init_flag_impl(volatile pg_atomic_flag *ptr) -{ - pg_atomic_write_u32_impl(ptr, 0); -} - -#define PG_HAVE_ATOMIC_TEST_SET_FLAG -static inline bool -pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) -{ - return pg_atomic_exchange_u32_impl(ptr, 1) == 0; -} - -#define PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG -static inline bool -pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *ptr) -{ - return pg_atomic_read_u32_impl(ptr) == 0; -} - - -#define PG_HAVE_ATOMIC_CLEAR_FLAG -static inline void -pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr) -{ - /* XXX: release semantics suffice? */ - pg_memory_barrier_impl(); - pg_atomic_write_u32_impl(ptr, 0); -} - -/* - * provide fallback for test_and_set using atomic_compare_exchange if - * available. - */ -#elif !defined(PG_HAVE_ATOMIC_TEST_SET_FLAG) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) - -#define PG_HAVE_ATOMIC_INIT_FLAG -static inline void -pg_atomic_init_flag_impl(volatile pg_atomic_flag *ptr) -{ - pg_atomic_write_u32_impl(ptr, 0); -} - -#define PG_HAVE_ATOMIC_TEST_SET_FLAG -static inline bool -pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) -{ - uint32 value = 0; - return pg_atomic_compare_exchange_u32_impl(ptr, &value, 1); -} - -#define PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG -static inline bool -pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *ptr) -{ - return pg_atomic_read_u32_impl(ptr) == 0; -} - -#define PG_HAVE_ATOMIC_CLEAR_FLAG -static inline void -pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr) -{ - /* XXX: release semantics suffice? */ - pg_memory_barrier_impl(); - pg_atomic_write_u32_impl(ptr, 0); -} - -#elif !defined(PG_HAVE_ATOMIC_TEST_SET_FLAG) -# error "No pg_atomic_test_and_set provided" -#endif /* !defined(PG_HAVE_ATOMIC_TEST_SET_FLAG) */ - - -#ifndef PG_HAVE_ATOMIC_INIT_U32 -#define PG_HAVE_ATOMIC_INIT_U32 -static inline void -pg_atomic_init_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val_) -{ - ptr->value = val_; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_EXCHANGE_U32) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) -#define PG_HAVE_ATOMIC_EXCHANGE_U32 -static inline uint32 -pg_atomic_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 xchg_) -{ - uint32 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u32_impl(ptr, &old, xchg_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_ADD_U32) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) -#define PG_HAVE_ATOMIC_FETCH_ADD_U32 -static inline uint32 -pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - uint32 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u32_impl(ptr, &old, old + add_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_SUB_U32) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) -#define PG_HAVE_ATOMIC_FETCH_SUB_U32 -static inline uint32 -pg_atomic_fetch_sub_u32_impl(volatile pg_atomic_uint32 *ptr, int32 sub_) -{ - return pg_atomic_fetch_add_u32_impl(ptr, -sub_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_AND_U32) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) -#define PG_HAVE_ATOMIC_FETCH_AND_U32 -static inline uint32 -pg_atomic_fetch_and_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 and_) -{ - uint32 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u32_impl(ptr, &old, old & and_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_OR_U32) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) -#define PG_HAVE_ATOMIC_FETCH_OR_U32 -static inline uint32 -pg_atomic_fetch_or_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 or_) -{ - uint32 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u32_impl(ptr, &old, old | or_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_ADD_FETCH_U32) && defined(PG_HAVE_ATOMIC_FETCH_ADD_U32) -#define PG_HAVE_ATOMIC_ADD_FETCH_U32 -static inline uint32 -pg_atomic_add_fetch_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) -{ - return pg_atomic_fetch_add_u32_impl(ptr, add_) + add_; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_SUB_FETCH_U32) && defined(PG_HAVE_ATOMIC_FETCH_SUB_U32) -#define PG_HAVE_ATOMIC_SUB_FETCH_U32 -static inline uint32 -pg_atomic_sub_fetch_u32_impl(volatile pg_atomic_uint32 *ptr, int32 sub_) -{ - return pg_atomic_fetch_sub_u32_impl(ptr, sub_) - sub_; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_READ_MEMBARRIER_U32) && defined(PG_HAVE_ATOMIC_FETCH_ADD_U32) -#define PG_HAVE_ATOMIC_READ_MEMBARRIER_U32 -static inline uint32 -pg_atomic_read_membarrier_u32_impl(volatile pg_atomic_uint32 *ptr) -{ - return pg_atomic_fetch_add_u32_impl(ptr, 0); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_WRITE_MEMBARRIER_U32) && defined(PG_HAVE_ATOMIC_EXCHANGE_U32) -#define PG_HAVE_ATOMIC_WRITE_MEMBARRIER_U32 -static inline void -pg_atomic_write_membarrier_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val) -{ - (void) pg_atomic_exchange_u32_impl(ptr, val); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_EXCHANGE_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) -#define PG_HAVE_ATOMIC_EXCHANGE_U64 -static inline uint64 -pg_atomic_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 xchg_) -{ - uint64 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u64_impl(ptr, &old, xchg_)) - /* skip */; - return old; -} -#endif - -#ifndef PG_HAVE_ATOMIC_WRITE_U64 -#define PG_HAVE_ATOMIC_WRITE_U64 - -#if defined(PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY) && \ - !defined(PG_HAVE_ATOMIC_U64_SIMULATION) - -static inline void -pg_atomic_write_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val) -{ - /* - * On this platform aligned 64bit writes are guaranteed to be atomic, - * except if using the fallback implementation, where can't guarantee the - * required alignment. - */ - AssertPointerAlignment(ptr, 8); - ptr->value = val; -} - -#else - -static inline void -pg_atomic_write_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val) -{ - /* - * 64 bit writes aren't safe on all platforms. In the generic - * implementation implement them as an atomic exchange. - */ - pg_atomic_exchange_u64_impl(ptr, val); -} - -#endif /* PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY && !PG_HAVE_ATOMIC_U64_SIMULATION */ -#endif /* PG_HAVE_ATOMIC_WRITE_U64 */ - -#ifndef PG_HAVE_ATOMIC_READ_U64 -#define PG_HAVE_ATOMIC_READ_U64 - -#if defined(PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY) && \ - !defined(PG_HAVE_ATOMIC_U64_SIMULATION) - -static inline uint64 -pg_atomic_read_u64_impl(volatile pg_atomic_uint64 *ptr) -{ - /* - * On this platform aligned 64-bit reads are guaranteed to be atomic. - */ - AssertPointerAlignment(ptr, 8); - return ptr->value; -} - -#else - -static inline uint64 -pg_atomic_read_u64_impl(volatile pg_atomic_uint64 *ptr) -{ - uint64 old = 0; - - /* - * 64-bit reads aren't atomic on all platforms. In the generic - * implementation implement them as a compare/exchange with 0. That'll - * fail or succeed, but always return the old value. Possibly might store - * a 0, but only if the previous value also was a 0 - i.e. harmless. - */ - pg_atomic_compare_exchange_u64_impl(ptr, &old, 0); - - return old; -} -#endif /* PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY && !PG_HAVE_ATOMIC_U64_SIMULATION */ -#endif /* PG_HAVE_ATOMIC_READ_U64 */ - -#ifndef PG_HAVE_ATOMIC_INIT_U64 -#define PG_HAVE_ATOMIC_INIT_U64 -static inline void -pg_atomic_init_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val_) -{ - ptr->value = val_; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_ADD_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) -#define PG_HAVE_ATOMIC_FETCH_ADD_U64 -static inline uint64 -pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) -{ - uint64 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u64_impl(ptr, &old, old + add_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_SUB_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) -#define PG_HAVE_ATOMIC_FETCH_SUB_U64 -static inline uint64 -pg_atomic_fetch_sub_u64_impl(volatile pg_atomic_uint64 *ptr, int64 sub_) -{ - return pg_atomic_fetch_add_u64_impl(ptr, -sub_); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_AND_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) -#define PG_HAVE_ATOMIC_FETCH_AND_U64 -static inline uint64 -pg_atomic_fetch_and_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 and_) -{ - uint64 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u64_impl(ptr, &old, old & and_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_FETCH_OR_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) -#define PG_HAVE_ATOMIC_FETCH_OR_U64 -static inline uint64 -pg_atomic_fetch_or_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 or_) -{ - uint64 old; - old = ptr->value; /* ok if read is not atomic */ - while (!pg_atomic_compare_exchange_u64_impl(ptr, &old, old | or_)) - /* skip */; - return old; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_ADD_FETCH_U64) && defined(PG_HAVE_ATOMIC_FETCH_ADD_U64) -#define PG_HAVE_ATOMIC_ADD_FETCH_U64 -static inline uint64 -pg_atomic_add_fetch_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) -{ - return pg_atomic_fetch_add_u64_impl(ptr, add_) + add_; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_SUB_FETCH_U64) && defined(PG_HAVE_ATOMIC_FETCH_SUB_U64) -#define PG_HAVE_ATOMIC_SUB_FETCH_U64 -static inline uint64 -pg_atomic_sub_fetch_u64_impl(volatile pg_atomic_uint64 *ptr, int64 sub_) -{ - return pg_atomic_fetch_sub_u64_impl(ptr, sub_) - sub_; -} -#endif - -#if !defined(PG_HAVE_ATOMIC_READ_MEMBARRIER_U64) && defined(PG_HAVE_ATOMIC_FETCH_ADD_U64) -#define PG_HAVE_ATOMIC_READ_MEMBARRIER_U64 -static inline uint64 -pg_atomic_read_membarrier_u64_impl(volatile pg_atomic_uint64 *ptr) -{ - return pg_atomic_fetch_add_u64_impl(ptr, 0); -} -#endif - -#if !defined(PG_HAVE_ATOMIC_WRITE_MEMBARRIER_U64) && defined(PG_HAVE_ATOMIC_EXCHANGE_U64) -#define PG_HAVE_ATOMIC_WRITE_MEMBARRIER_U64 -static inline void -pg_atomic_write_membarrier_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val) -{ - (void) pg_atomic_exchange_u64_impl(ptr, val); -} -#endif diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 23bce72ae64..3b2c109e67b 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3862,6 +3862,8 @@ pending_label pgParameterStatus pgoff_t pg_atomic_flag +pg_atomic_uint8 +pg_atomic_uint16 pg_atomic_uint32 pg_atomic_uint64 pg_be_sasl_mech -- 2.50.1 (Apple Git-155)