public inbox for [email protected]
help / color / mirror / Atom feedFrom: Thomas Munro <[email protected]>
To: Heikki Linnakangas <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Subject: Re: Trying out <stdatomic.h>
Date: Fri, 14 Nov 2025 00:35:48 +1300
Message-ID: <CA+hUKG+bub1T9TFLUAJHr+Z519VRgJS5tb10xBxEtJZRH8GXiA@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CA+hUKGKFvu3zyvv3aaj5hHs9VtWcjFAmisOwOc7aOZNc5AF3NA@mail.gmail.com>
<[email protected]>
On Tue, Nov 11, 2025 at 2:57 AM Heikki Linnakangas <[email protected]> wrote:
> > [PATCH v1 1/4] Add some missing #include <limits.h>.
>
> This seems like a good thing regardless of the other patches.
Pushed.
> The patch removes 'src/template/solaris'. Is that on purpose?
Fixed.
It passes with VS 2022 on CI. I had to skip some assertions about
macros promising lock-free implementation, that it doesn't define in C
mode yet. They are definitely lock-free though[1], and the macros are
defined for C++, and the same under the covers... Perhaps
feature/conformance macros won't be defined until a few remaining
pieces (things we don't care about) are accessible from C? (I see
that Visual Studio 2026 has also just shipped a couple of days ago,
not investigated.)
[1] https://devblogs.microsoft.com/cppblog/c11-atomics-in-visual-studio-2022-version-17-5-preview-2/
Attachments:
[application/octet-stream] v2-0001-ci-Use-Visual-Studio-2022-for-stdatomic.h.patch (1.2K, 2-v2-0001-ci-Use-Visual-Studio-2022-for-stdatomic.h.patch)
download | inline diff:
From dd4bea7fd8c1e09ce6d8a66323b30c82e4a568c1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Thu, 13 Nov 2025 17:48:09 +1300
Subject: [PATCH v2 1/5] ci: Use Visual Studio 2022, for <stdatomic.h>.
XXX No opinion on when we could require this as a baseline. We only
just pulled the trigger on Visual Studio 2019 which added lots of C11,
but lacked <stdatomic.h>.
(Thanks to Bilal for getting this onto the CI image!)
---
.cirrus.tasks.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml
index 2fe9671f3dc..266ce99e7db 100644
--- a/.cirrus.tasks.yml
+++ b/.cirrus.tasks.yml
@@ -767,11 +767,12 @@ WINDOWS_ENVIRONMENT_BASE: &WINDOWS_ENVIRONMENT_BASE
task:
- name: Windows - Server 2022, VS 2019 - Meson & ninja
+ name: Windows - Server 2022, VS 2022 - Meson & ninja
<< : *WINDOWS_ENVIRONMENT_BASE
env:
TEST_JOBS: 8 # wild guess, data based value welcome
+ PATH: C:\VS_2022\VC\Auxiliary\Build;${PATH}
# Cirrus defaults to SetErrorMode(SEM_NOGPFAULTERRORBOX | ...). That
# prevents crash reporting from working unless binaries do SetErrorMode()
--
2.50.1 (Apple Git-155)
[application/octet-stream] v2-0002-Enable-Visual-Studio-s-stdatomic.h-support.patch (790B, 3-v2-0002-Enable-Visual-Studio-s-stdatomic.h-support.patch)
download | inline diff:
From 58f088ee11236b3b5b20f2b6ffd97d17181f2afc Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Thu, 13 Nov 2025 10:48:14 +1300
Subject: [PATCH v2 2/5] Enable Visual Studio's <stdatomic.h> support.
XXX It's still marked as 'experimental' as of the compiler in CI.
---
meson.build | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/meson.build b/meson.build
index c1e17aa3040..04c563378c7 100644
--- a/meson.build
+++ b/meson.build
@@ -580,6 +580,10 @@ if not cc.compiles(c11_test, name: 'C11')
endif
endif
+# XXX Should test whether <stdatomic.h> works without this
+if cc.get_id() == 'msvc'
+ cflags += '/experimental:c11atomics'
+endif
postgres_inc = [include_directories(postgres_inc_d)]
test_lib_d = postgres_lib_d
--
2.50.1 (Apple Git-155)
[application/octet-stream] v2-0003-Redefine-port-atomics.h-on-top-of-stdatomic.h.patch (68.1K, 4-v2-0003-Redefine-port-atomics.h-on-top-of-stdatomic.h.patch)
download | inline diff:
From f7274f7320a0b1231dd3e676e2b40d2235481189 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Tue, 2 Jul 2024 22:31:42 +1200
Subject: [PATCH v2 3/5] Redefine port/atomics.h on top of <stdatomic.h>.
* Redirect types and functions to standard C/C++
* Use <atomic> 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 <stdatomic.h>, 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 <stdatomic.h>. Use approach from C++23 33.5.12. */
+extern "C++"
+{
+#include <atomic>
+#define pg_atomic(T) std::atomic<T>
+ 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 <stdatomic.h>
+#define pg_atomic(T) _Atomic(T)
#endif
-#define INSIDE_ATOMICS_H
+/*
+ * Visual Studio 2022 doesn't seem to define these in <stdatomic.h> yet, but
+ * does define them to 2 in <atomic>. 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 <limits.h>
+/* 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 <stdatomic.h>, 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 <stdatomic.h>.
*/
-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)
[application/octet-stream] v2-0004-Use-atomics-API-to-implement-spinlocks.patch (35.8K, 5-v2-0004-Use-atomics-API-to-implement-spinlocks.patch)
download | inline diff:
From 4727999c79932e728582ccbfe13e9c5ae0620ffe Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 31 Jul 2024 13:11:35 +1200
Subject: [PATCH v2 4/5] Use atomics API to implement spinlocks.
Since our spinlock API pre-dates our C11-inspired atomics API by
decades, it had its own hand-crafted operations written in assembler.
Use the modern atomics API instead, to simplify and de-duplicate. We
couldn't have done this until fairly recently, because that would have
been be circular: atomics were simulated with spinlocks in
--disable-atomics builds. Commit 81385261 removed that option, so now
we can delete most of the system-specific spinlock code and use
pg_atomic_flag.
The remaining architecture-specific knowledge is moved into
src/include/port/spin_delay.h:
* implementation of pg_spin_delay()
* whether it is believed to be a good idea to perform a relaxed load
before attempting test-and-set while spinning (carried forward from
the old hand-crafted code)
WIP!
---
src/backend/port/meson.build | 2 +-
src/backend/port/tas/dummy.s | 0
src/backend/storage/lmgr/s_lock.c | 128 +-----
src/include/port/spin_delay.h | 91 ++++
src/include/storage/s_lock.h | 737 ------------------------------
src/include/storage/spin.h | 70 ++-
src/test/regress/regress.c | 25 +-
7 files changed, 176 insertions(+), 877 deletions(-)
delete mode 100644 src/backend/port/tas/dummy.s
create mode 100644 src/include/port/spin_delay.h
delete mode 100644 src/include/storage/s_lock.h
diff --git a/src/backend/port/meson.build b/src/backend/port/meson.build
index 09d54e01d13..45438fcec17 100644
--- a/src/backend/port/meson.build
+++ b/src/backend/port/meson.build
@@ -30,4 +30,4 @@ if host_system == 'windows'
endif
# autoconf generates the file there, ensure we get a conflict
-generated_sources_ac += {'src/backend/port': ['pg_sema.c', 'pg_shmem.c', 'tas.s']}
+generated_sources_ac += {'src/backend/port': ['pg_sema.c', 'pg_shmem.c']}
diff --git a/src/backend/port/tas/dummy.s b/src/backend/port/tas/dummy.s
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index d26e192f4bc..a2fc129c688 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -51,7 +51,9 @@
#include <unistd.h>
#include "common/pg_prng.h"
-#include "storage/s_lock.h"
+#include "port/atomics.h"
+#include "port/spin_delay.h"
+#include "storage/spin.h"
#include "utils/wait_event.h"
#define MIN_SPINS_PER_DELAY 10
@@ -92,7 +94,7 @@ s_lock_stuck(const char *file, int line, const char *func)
}
/*
- * s_lock(lock) - platform-independent portion of waiting for a spinlock.
+ * s_lock(lock) - out-of-line portion of waiting for a spinlock.
*/
int
s_lock(volatile slock_t *lock, const char *file, int line, const char *func)
@@ -101,8 +103,25 @@ s_lock(volatile slock_t *lock, const char *file, int line, const char *func)
init_spin_delay(&delayStatus, file, line, func);
- while (TAS_SPIN(lock))
+ for (;;)
{
+ bool try_to_set;
+
+#ifdef PG_SPIN_TRY_RELAXED
+
+ /*
+ * It is known to be more efficient to test the lock with a relaxed
+ * load first, while spinning, on this platform.
+ */
+ try_to_set = pg_atomic_unlocked_test_flag(lock);
+#else
+ try_to_set = true;
+#endif
+
+ /* Try to get the lock. */
+ if (try_to_set && pg_atomic_test_set_flag(lock))
+ break;
+
perform_spin_delay(&delayStatus);
}
@@ -111,14 +130,6 @@ s_lock(volatile slock_t *lock, const char *file, int line, const char *func)
return delayStatus.delays;
}
-#ifdef USE_DEFAULT_S_UNLOCK
-void
-s_unlock(volatile slock_t *lock)
-{
- *lock = 0;
-}
-#endif
-
/*
* Wait while spinning on a contended spinlock.
*/
@@ -126,7 +137,7 @@ void
perform_spin_delay(SpinDelayStatus *status)
{
/* CPU-specific delay each time through the loop */
- SPIN_DELAY();
+ pg_spin_delay();
/* Block the process every spins_per_delay tries */
if (++(status->spins) >= spins_per_delay)
@@ -229,96 +240,3 @@ update_spins_per_delay(int shared_spins_per_delay)
*/
return (shared_spins_per_delay * 15 + spins_per_delay) / 16;
}
-
-
-/*****************************************************************************/
-#if defined(S_LOCK_TEST)
-
-/*
- * test program for verifying a port's spinlock support.
- */
-
-struct test_lock_struct
-{
- char pad1;
- slock_t lock;
- char pad2;
-};
-
-volatile struct test_lock_struct test_lock;
-
-int
-main()
-{
- pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
-
- test_lock.pad1 = test_lock.pad2 = 0x44;
-
- S_INIT_LOCK(&test_lock.lock);
-
- if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
- {
- printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
- return 1;
- }
-
- if (!S_LOCK_FREE(&test_lock.lock))
- {
- printf("S_LOCK_TEST: failed, lock not initialized\n");
- return 1;
- }
-
- S_LOCK(&test_lock.lock);
-
- if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
- {
- printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
- return 1;
- }
-
- if (S_LOCK_FREE(&test_lock.lock))
- {
- printf("S_LOCK_TEST: failed, lock not locked\n");
- return 1;
- }
-
- S_UNLOCK(&test_lock.lock);
-
- if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
- {
- printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
- return 1;
- }
-
- if (!S_LOCK_FREE(&test_lock.lock))
- {
- printf("S_LOCK_TEST: failed, lock not unlocked\n");
- return 1;
- }
-
- S_LOCK(&test_lock.lock);
-
- if (test_lock.pad1 != 0x44 || test_lock.pad2 != 0x44)
- {
- printf("S_LOCK_TEST: failed, declared datatype is wrong size\n");
- return 1;
- }
-
- if (S_LOCK_FREE(&test_lock.lock))
- {
- printf("S_LOCK_TEST: failed, lock not re-locked\n");
- return 1;
- }
-
- printf("S_LOCK_TEST: this will print %d stars and then\n", NUM_DELAYS);
- printf(" exit with a 'stuck spinlock' message\n");
- printf(" if S_LOCK() and TAS() are working.\n");
- fflush(stdout);
-
- s_lock(&test_lock.lock, __FILE__, __LINE__, __func__);
-
- printf("S_LOCK_TEST: failed, lock not locked\n");
- return 1;
-}
-
-#endif /* S_LOCK_TEST */
diff --git a/src/include/port/spin_delay.h b/src/include/port/spin_delay.h
new file mode 100644
index 00000000000..c583698af8e
--- /dev/null
+++ b/src/include/port/spin_delay.h
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------------------------
+ *
+ * spin_delay.h
+ * Implementation of architecture-specific spinlock delay.
+ *
+ * Note to implementors: the default implementation does nothing.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/port/spin_delay.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SPIN_DELAY_H
+#define SPIN_DELAY_H
+
+static pg_attribute_always_inline void
+pg_spin_delay(void)
+{
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+#ifdef __i386__ /* 32-bit i386 */
+ /*
+ * 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.
+ */
+ __asm__ __volatile__(
+ " rep; nop \n");
+#endif /* __i386__ */
+#ifdef __x86_64__ /* AMD Opteron, Intel EM64T */
+
+ /*
+ * Adding a PAUSE in the spin delay loop is demonstrably a no-op on
+ * Opteron, but it may be of some use on EM64T, so we keep it.
+ */
+ __asm__ __volatile__(
+ " rep; nop \n");
+#endif /* __x86_64__ */
+#if defined(__aarch64__)
+
+ /*
+ * Using an ISB instruction to delay in spinlock loops appears beneficial
+ * on high-core-count ARM64 processors. It seems mostly a wash for
+ * smaller gear, and ISB doesn't exist at all on pre-v7 ARM chips.
+ */
+ __asm__ __volatile__(
+ " isb; \n");
+#endif /* __aarch64__ */
+#endif /* defined(__GNUC__) ||
+ * defined(__INTEL_COMPILER) */
+
+#ifdef _MSC_VER
+
+ /*
+ * If using Visual C++ on Win64, inline assembly is unavailable. Use a
+ * _mm_pause intrinsic instead of rep nop.
+ */
+#if defined(_WIN64)
+ _mm_pause();
+#else
+ /* See comment for gcc code. Same code, MASM syntax */
+ __asm rep nop;
+#endif
+#endif /* _MSC_VER */
+}
+
+/* Architectures on which a relaxed load is recommended while spinning. */
+#if defined(__i386__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_AMD64) || \
+ defined(__ppc__) || defined(__powerpc__) || \
+ defined(__ppc64__) || defined(__powerpc64__)
+#define PG_SPIN_TRY_RELAXED
+#endif
+
+#endif /* SPIN_DELAY_H */
diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h
deleted file mode 100644
index 7f8f566bd40..00000000000
--- a/src/include/storage/s_lock.h
+++ /dev/null
@@ -1,737 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * s_lock.h
- * Implementation of spinlocks.
- *
- * NOTE: none of the macros in this file are intended to be called directly.
- * Call them through the macros in spin.h.
- *
- * The following hardware-dependent macros must be provided for each
- * supported platform:
- *
- * void S_INIT_LOCK(slock_t *lock)
- * Initialize a spinlock (to the unlocked state).
- *
- * int S_LOCK(slock_t *lock)
- * Acquire a spinlock, waiting if necessary.
- * Time out and abort() if unable to acquire the lock in a
- * "reasonable" amount of time --- typically ~ 1 minute.
- * Should return number of "delays"; see s_lock.c
- *
- * void S_UNLOCK(slock_t *lock)
- * Unlock a previously acquired lock.
- *
- * bool S_LOCK_FREE(slock_t *lock)
- * Tests if the lock is free. Returns true if free, false if locked.
- * This does *not* change the state of the lock.
- *
- * void SPIN_DELAY(void)
- * Delay operation to occur inside spinlock wait loop.
- *
- * Note to implementors: there are default implementations for all these
- * macros at the bottom of the file. Check if your platform can use
- * these or needs to override them.
- *
- * Usually, S_LOCK() is implemented in terms of even lower-level macros
- * TAS() and TAS_SPIN():
- *
- * int TAS(slock_t *lock)
- * Atomic test-and-set instruction. Attempt to acquire the lock,
- * but do *not* wait. Returns 0 if successful, nonzero if unable
- * to acquire the lock.
- *
- * int TAS_SPIN(slock_t *lock)
- * Like TAS(), but this version is used when waiting for a lock
- * previously found to be contended. By default, this is the
- * same as TAS(), but on some architectures it's better to poll a
- * contended lock using an unlocked instruction and retry the
- * atomic test-and-set only when it appears free.
- *
- * TAS() and TAS_SPIN() are NOT part of the API, and should never be called
- * directly.
- *
- * CAUTION: on some platforms TAS() and/or TAS_SPIN() may sometimes report
- * failure to acquire a lock even when the lock is not locked. For example,
- * on Alpha TAS() will "fail" if interrupted. Therefore a retry loop must
- * always be used, even if you are certain the lock is free.
- *
- * It is the responsibility of these macros to make sure that the compiler
- * does not re-order accesses to shared memory to precede the actual lock
- * acquisition, or follow the lock release. Prior to PostgreSQL 9.5, this
- * was the caller's responsibility, which meant that callers had to use
- * volatile-qualified pointers to refer to both the spinlock itself and the
- * shared data being accessed within the spinlocked critical section. This
- * was notationally awkward, easy to forget (and thus error-prone), and
- * prevented some useful compiler optimizations. For these reasons, we
- * now require that the macros themselves prevent compiler re-ordering,
- * so that the caller doesn't need to take special precautions.
- *
- * On platforms with weak memory ordering, the TAS(), TAS_SPIN(), and
- * S_UNLOCK() macros must further include hardware-level memory fence
- * instructions to prevent similar re-ordering at the hardware level.
- * TAS() and TAS_SPIN() must guarantee that loads and stores issued after
- * the macro are not executed until the lock has been obtained. Conversely,
- * S_UNLOCK() must guarantee that loads and stores issued before the macro
- * have been executed before the lock is released.
- *
- * On most supported platforms, TAS() uses a tas() function written
- * in assembly language to execute a hardware atomic-test-and-set
- * instruction. Equivalent OS-supplied mutex routines could be used too.
- *
- *
- * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/include/storage/s_lock.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef S_LOCK_H
-#define S_LOCK_H
-
-#ifdef FRONTEND
-#error "s_lock.h may not be included from frontend code"
-#endif
-
-#if defined(__GNUC__) || defined(__INTEL_COMPILER)
-/*************************************************************************
- * All the gcc inlines
- * Gcc consistently defines the CPU as __cpu__.
- * Other compilers use __cpu or __cpu__ so we test for both in those cases.
- */
-
-/*----------
- * Standard gcc asm format (assuming "volatile slock_t *lock"):
-
- __asm__ __volatile__(
- " instruction \n"
- " instruction \n"
- " instruction \n"
-: "=r"(_res), "+m"(*lock) // return register, in/out lock value
-: "r"(lock) // lock pointer, in input register
-: "memory", "cc"); // show clobbered registers here
-
- * The output-operands list (after first colon) should always include
- * "+m"(*lock), whether or not the asm code actually refers to this
- * operand directly. This ensures that gcc believes the value in the
- * lock variable is used and set by the asm code. Also, the clobbers
- * list (after third colon) should always include "memory"; this prevents
- * gcc from thinking it can cache the values of shared-memory fields
- * across the asm code. Add "cc" if your asm code changes the condition
- * code register, and also list any temp registers the code uses.
- *----------
- */
-
-
-#ifdef __i386__ /* 32-bit i386 */
-#define HAS_TEST_AND_SET
-
-typedef unsigned char slock_t;
-
-#define TAS(lock) tas(lock)
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- slock_t _res = 1;
-
- /*
- * Use a non-locking test before asserting the bus lock. Note that the
- * extra test appears to be a small loss on some x86 platforms and a small
- * win on others; it's by no means clear that we should keep it.
- *
- * When this was last tested, we didn't have separate TAS() and TAS_SPIN()
- * macros. Nowadays it probably would be better to do a non-locking test
- * in TAS_SPIN() but not in TAS(), like on x86_64, but no-one's done the
- * testing to verify that. Without some empirical evidence, better to
- * leave it alone.
- */
- __asm__ __volatile__(
- " cmpb $0,%1 \n"
- " jne 1f \n"
- " lock \n"
- " xchgb %0,%1 \n"
- "1: \n"
-: "+q"(_res), "+m"(*lock)
-: /* no inputs */
-: "memory", "cc");
- return (int) _res;
-}
-
-#define SPIN_DELAY() spin_delay()
-
-static __inline__ void
-spin_delay(void)
-{
- /*
- * 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.
- */
- __asm__ __volatile__(
- " rep; nop \n");
-}
-
-#endif /* __i386__ */
-
-
-#ifdef __x86_64__ /* AMD Opteron, Intel EM64T */
-#define HAS_TEST_AND_SET
-
-typedef unsigned char slock_t;
-
-#define TAS(lock) tas(lock)
-
-/*
- * On Intel EM64T, it's a win to use a non-locking test before the xchg proper,
- * but only when spinning.
- *
- * See also Implementing Scalable Atomic Locks for Multi-Core Intel(tm) EM64T
- * and IA32, by Michael Chynoweth and Mary R. Lee. As of this writing, it is
- * available at:
- * http://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32-architectures
- */
-#define TAS_SPIN(lock) (*(lock) ? 1 : TAS(lock))
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- slock_t _res = 1;
-
- __asm__ __volatile__(
- " lock \n"
- " xchgb %0,%1 \n"
-: "+q"(_res), "+m"(*lock)
-: /* no inputs */
-: "memory", "cc");
- return (int) _res;
-}
-
-#define SPIN_DELAY() spin_delay()
-
-static __inline__ void
-spin_delay(void)
-{
- /*
- * Adding a PAUSE in the spin delay loop is demonstrably a no-op on
- * Opteron, but it may be of some use on EM64T, so we keep it.
- */
- __asm__ __volatile__(
- " rep; nop \n");
-}
-
-#endif /* __x86_64__ */
-
-
-/*
- * On ARM and ARM64, we use __sync_lock_test_and_set(int *, int) if available.
- *
- * We use the int-width variant of the builtin because it works on more chips
- * than other widths.
- */
-#if defined(__arm__) || defined(__arm) || defined(__aarch64__)
-#ifdef HAVE_GCC__SYNC_INT32_TAS
-#define HAS_TEST_AND_SET
-
-#define TAS(lock) tas(lock)
-
-typedef int slock_t;
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- return __sync_lock_test_and_set(lock, 1);
-}
-
-#define S_UNLOCK(lock) __sync_lock_release(lock)
-
-#if defined(__aarch64__)
-
-/*
- * On ARM64, it's a win to use a non-locking test before the TAS proper. It
- * may be a win on 32-bit ARM, too, but nobody's tested it yet.
- */
-#define TAS_SPIN(lock) (*(lock) ? 1 : TAS(lock))
-
-#define SPIN_DELAY() spin_delay()
-
-static __inline__ void
-spin_delay(void)
-{
- /*
- * Using an ISB instruction to delay in spinlock loops appears beneficial
- * on high-core-count ARM64 processors. It seems mostly a wash for smaller
- * gear, and ISB doesn't exist at all on pre-v7 ARM chips.
- */
- __asm__ __volatile__(
- " isb; \n");
-}
-
-#endif /* __aarch64__ */
-#endif /* HAVE_GCC__SYNC_INT32_TAS */
-#endif /* __arm__ || __arm || __aarch64__ */
-
-
-/* S/390 and S/390x Linux (32- and 64-bit zSeries) */
-#if defined(__s390__) || defined(__s390x__)
-#define HAS_TEST_AND_SET
-
-typedef unsigned int slock_t;
-
-#define TAS(lock) tas(lock)
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- int _res = 0;
-
- __asm__ __volatile__(
- " cs %0,%3,0(%2) \n"
-: "+d"(_res), "+m"(*lock)
-: "a"(lock), "d"(1)
-: "memory", "cc");
- return _res;
-}
-
-#endif /* __s390__ || __s390x__ */
-
-
-#if defined(__sparc__) /* Sparc */
-/*
- * Solaris has always run sparc processors in TSO (total store) mode, but
- * linux didn't use to and the *BSDs still don't. So, be careful about
- * acquire/release semantics. The CPU will treat superfluous members as
- * NOPs, so it's just code space.
- */
-#define HAS_TEST_AND_SET
-
-typedef unsigned char slock_t;
-
-#define TAS(lock) tas(lock)
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- slock_t _res;
-
- /*
- * "cas" would be better than "ldstub", but it is only present on
- * sparcv8plus and later, while some platforms still support sparcv7 or
- * sparcv8. Also, "cas" requires that the system be running in TSO mode.
- */
- __asm__ __volatile__(
- " ldstub [%2], %0 \n"
-: "=r"(_res), "+m"(*lock)
-: "r"(lock)
-: "memory");
-#if defined(__sparcv7) || defined(__sparc_v7__)
- /*
- * No stbar or membar available, luckily no actually produced hardware
- * requires a barrier.
- */
-#elif defined(__sparcv8) || defined(__sparc_v8__)
- /* stbar is available (and required for both PSO, RMO), membar isn't */
- __asm__ __volatile__ ("stbar \n":::"memory");
-#else
- /*
- * #LoadStore (RMO) | #LoadLoad (RMO) together are the appropriate acquire
- * barrier for sparcv8+ upwards.
- */
- __asm__ __volatile__ ("membar #LoadStore | #LoadLoad \n":::"memory");
-#endif
- return (int) _res;
-}
-
-#if defined(__sparcv7) || defined(__sparc_v7__)
-/*
- * No stbar or membar available, luckily no actually produced hardware
- * requires a barrier. We fall through to the default gcc definition of
- * S_UNLOCK in this case.
- */
-#elif defined(__sparcv8) || defined(__sparc_v8__)
-/* stbar is available (and required for both PSO, RMO), membar isn't */
-#define S_UNLOCK(lock) \
-do \
-{ \
- __asm__ __volatile__ ("stbar \n":::"memory"); \
- *((volatile slock_t *) (lock)) = 0; \
-} while (0)
-#else
-/*
- * #LoadStore (RMO) | #StoreStore (RMO, PSO) together are the appropriate
- * release barrier for sparcv8+ upwards.
- */
-#define S_UNLOCK(lock) \
-do \
-{ \
- __asm__ __volatile__ ("membar #LoadStore | #StoreStore \n":::"memory"); \
- *((volatile slock_t *) (lock)) = 0; \
-} while (0)
-#endif
-
-#endif /* __sparc__ */
-
-
-/* PowerPC */
-#if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__)
-#define HAS_TEST_AND_SET
-
-typedef unsigned int slock_t;
-
-#define TAS(lock) tas(lock)
-
-/* On PPC, it's a win to use a non-locking test before the lwarx */
-#define TAS_SPIN(lock) (*(lock) ? 1 : TAS(lock))
-
-/*
- * The second operand of addi can hold a constant zero or a register number,
- * hence constraint "=&b" to avoid allocating r0. "b" stands for "address
- * base register"; most operands having this register-or-zero property are
- * address bases, e.g. the second operand of lwax.
- *
- * NOTE: per the Enhanced PowerPC Architecture manual, v1.0 dated 7-May-2002,
- * an isync is a sufficient synchronization barrier after a lwarx/stwcx loop.
- * But if the spinlock is in ordinary memory, we can use lwsync instead for
- * better performance.
- */
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- slock_t _t;
- int _res;
-
- __asm__ __volatile__(
-" lwarx %0,0,%3,1 \n"
-" cmpwi %0,0 \n"
-" bne 1f \n"
-" addi %0,%0,1 \n"
-" stwcx. %0,0,%3 \n"
-" beq 2f \n"
-"1: \n"
-" li %1,1 \n"
-" b 3f \n"
-"2: \n"
-" lwsync \n"
-" li %1,0 \n"
-"3: \n"
-: "=&b"(_t), "=r"(_res), "+m"(*lock)
-: "r"(lock)
-: "memory", "cc");
- return _res;
-}
-
-/*
- * PowerPC S_UNLOCK is almost standard but requires a "sync" instruction.
- * But we can use lwsync instead for better performance.
- */
-#define S_UNLOCK(lock) \
-do \
-{ \
- __asm__ __volatile__ (" lwsync \n" ::: "memory"); \
- *((volatile slock_t *) (lock)) = 0; \
-} while (0)
-
-#endif /* powerpc */
-
-
-#if defined(__mips__) && !defined(__sgi) /* non-SGI MIPS */
-#define HAS_TEST_AND_SET
-
-typedef unsigned int slock_t;
-
-#define TAS(lock) tas(lock)
-
-/*
- * Original MIPS-I processors lacked the LL/SC instructions, but if we are
- * so unfortunate as to be running on one of those, we expect that the kernel
- * will handle the illegal-instruction traps and emulate them for us. On
- * anything newer (and really, MIPS-I is extinct) LL/SC is the only sane
- * choice because any other synchronization method must involve a kernel
- * call. Unfortunately, many toolchains still default to MIPS-I as the
- * codegen target; if the symbol __mips shows that that's the case, we
- * have to force the assembler to accept LL/SC.
- *
- * R10000 and up processors require a separate SYNC, which has the same
- * issues as LL/SC.
- */
-#if __mips < 2
-#define MIPS_SET_MIPS2 " .set mips2 \n"
-#else
-#define MIPS_SET_MIPS2
-#endif
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- volatile slock_t *_l = lock;
- int _res;
- int _tmp;
-
- __asm__ __volatile__(
- " .set push \n"
- MIPS_SET_MIPS2
- " .set noreorder \n"
- " .set nomacro \n"
- " ll %0, %2 \n"
- " or %1, %0, 1 \n"
- " sc %1, %2 \n"
- " xori %1, 1 \n"
- " or %0, %0, %1 \n"
- " sync \n"
- " .set pop "
-: "=&r" (_res), "=&r" (_tmp), "+R" (*_l)
-: /* no inputs */
-: "memory");
- return _res;
-}
-
-/* MIPS S_UNLOCK is almost standard but requires a "sync" instruction */
-#define S_UNLOCK(lock) \
-do \
-{ \
- __asm__ __volatile__( \
- " .set push \n" \
- MIPS_SET_MIPS2 \
- " .set noreorder \n" \
- " .set nomacro \n" \
- " sync \n" \
- " .set pop " \
-: /* no outputs */ \
-: /* no inputs */ \
-: "memory"); \
- *((volatile slock_t *) (lock)) = 0; \
-} while (0)
-
-#endif /* __mips__ && !__sgi */
-
-
-
-/*
- * If we have no platform-specific knowledge, but we found that the compiler
- * provides __sync_lock_test_and_set(), use that. Prefer the int-width
- * version over the char-width version if we have both, on the rather dubious
- * grounds that that's known to be more likely to work in the ARM ecosystem.
- * (But we dealt with ARM above.)
- */
-#if !defined(HAS_TEST_AND_SET)
-
-#if defined(HAVE_GCC__SYNC_INT32_TAS)
-#define HAS_TEST_AND_SET
-
-#define TAS(lock) tas(lock)
-
-typedef int slock_t;
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- return __sync_lock_test_and_set(lock, 1);
-}
-
-#define S_UNLOCK(lock) __sync_lock_release(lock)
-
-#elif defined(HAVE_GCC__SYNC_CHAR_TAS)
-#define HAS_TEST_AND_SET
-
-#define TAS(lock) tas(lock)
-
-typedef char slock_t;
-
-static __inline__ int
-tas(volatile slock_t *lock)
-{
- return __sync_lock_test_and_set(lock, 1);
-}
-
-#define S_UNLOCK(lock) __sync_lock_release(lock)
-
-#endif /* HAVE_GCC__SYNC_INT32_TAS */
-
-#endif /* !defined(HAS_TEST_AND_SET) */
-
-
-/*
- * Default implementation of S_UNLOCK() for gcc/icc.
- *
- * Note that this implementation is unsafe for any platform that can reorder
- * a memory access (either load or store) after a following store. That
- * happens not to be possible on x86 and most legacy architectures (some are
- * single-processor!), but many modern systems have weaker memory ordering.
- * Those that do must define their own version of S_UNLOCK() rather than
- * relying on this one.
- */
-#if !defined(S_UNLOCK)
-#define S_UNLOCK(lock) \
- do { __asm__ __volatile__("" : : : "memory"); *(lock) = 0; } while (0)
-#endif
-
-#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */
-
-
-/*
- * ---------------------------------------------------------------------
- * Platforms that use non-gcc inline assembly:
- * ---------------------------------------------------------------------
- */
-
-#if !defined(HAS_TEST_AND_SET) /* We didn't trigger above, let's try here */
-
-#ifdef _MSC_VER
-typedef LONG slock_t;
-
-#define HAS_TEST_AND_SET
-#define TAS(lock) (InterlockedCompareExchange(lock, 1, 0))
-
-#define SPIN_DELAY() spin_delay()
-
-/* If using Visual C++ on Win64, inline assembly is unavailable.
- * Use a _mm_pause intrinsic instead of rep nop.
- */
-#if defined(_WIN64)
-static __forceinline void
-spin_delay(void)
-{
- _mm_pause();
-}
-#else
-static __forceinline void
-spin_delay(void)
-{
- /* See comment for gcc code. Same code, MASM syntax */
- __asm rep nop;
-}
-#endif
-
-#include <intrin.h>
-#pragma intrinsic(_ReadWriteBarrier)
-
-#define S_UNLOCK(lock) \
- do { _ReadWriteBarrier(); (*(lock)) = 0; } while (0)
-
-#endif
-
-
-#endif /* !defined(HAS_TEST_AND_SET) */
-
-
-/* Blow up if we didn't have any way to do spinlocks */
-#ifndef HAS_TEST_AND_SET
-#error PostgreSQL does not have spinlock support on this platform. Please report this to [email protected].
-#endif
-
-
-/*
- * Default Definitions - override these above as needed.
- */
-
-#if !defined(S_LOCK)
-#define S_LOCK(lock) \
- (TAS(lock) ? s_lock((lock), __FILE__, __LINE__, __func__) : 0)
-#endif /* S_LOCK */
-
-#if !defined(S_LOCK_FREE)
-#define S_LOCK_FREE(lock) (*(lock) == 0)
-#endif /* S_LOCK_FREE */
-
-#if !defined(S_UNLOCK)
-/*
- * Our default implementation of S_UNLOCK is essentially *(lock) = 0. This
- * is unsafe if the platform can reorder a memory access (either load or
- * store) after a following store; platforms where this is possible must
- * define their own S_UNLOCK. But CPU reordering is not the only concern:
- * if we simply defined S_UNLOCK() as an inline macro, the compiler might
- * reorder instructions from inside the critical section to occur after the
- * lock release. Since the compiler probably can't know what the external
- * function s_unlock is doing, putting the same logic there should be adequate.
- * A sufficiently-smart globally optimizing compiler could break that
- * assumption, though, and the cost of a function call for every spinlock
- * release may hurt performance significantly, so we use this implementation
- * only for platforms where we don't know of a suitable intrinsic. For the
- * most part, those are relatively obscure platform/compiler combinations to
- * which the PostgreSQL project does not have access.
- */
-#define USE_DEFAULT_S_UNLOCK
-extern void s_unlock(volatile slock_t *lock);
-#define S_UNLOCK(lock) s_unlock(lock)
-#endif /* S_UNLOCK */
-
-#if !defined(S_INIT_LOCK)
-#define S_INIT_LOCK(lock) S_UNLOCK(lock)
-#endif /* S_INIT_LOCK */
-
-#if !defined(SPIN_DELAY)
-#define SPIN_DELAY() ((void) 0)
-#endif /* SPIN_DELAY */
-
-#if !defined(TAS)
-extern int tas(volatile slock_t *lock); /* in port/.../tas.s, or
- * s_lock.c */
-
-#define TAS(lock) tas(lock)
-#endif /* TAS */
-
-#if !defined(TAS_SPIN)
-#define TAS_SPIN(lock) TAS(lock)
-#endif /* TAS_SPIN */
-
-
-/*
- * Platform-independent out-of-line support routines
- */
-extern int s_lock(volatile slock_t *lock, const char *file, int line, const char *func);
-
-/* Support for dynamic adjustment of spins_per_delay */
-#define DEFAULT_SPINS_PER_DELAY 100
-
-extern void set_spins_per_delay(int shared_spins_per_delay);
-extern int update_spins_per_delay(int shared_spins_per_delay);
-
-/*
- * Support for spin delay which is useful in various places where
- * spinlock-like procedures take place.
- */
-typedef struct
-{
- int spins;
- int delays;
- int cur_delay;
- const char *file;
- int line;
- const char *func;
-} SpinDelayStatus;
-
-static inline void
-init_spin_delay(SpinDelayStatus *status,
- const char *file, int line, const char *func)
-{
- status->spins = 0;
- status->delays = 0;
- status->cur_delay = 0;
- status->file = file;
- status->line = line;
- status->func = func;
-}
-
-#define init_local_spin_delay(status) init_spin_delay(status, __FILE__, __LINE__, __func__)
-extern void perform_spin_delay(SpinDelayStatus *status);
-extern void finish_spin_delay(SpinDelayStatus *status);
-
-#endif /* S_LOCK_H */
diff --git a/src/include/storage/spin.h b/src/include/storage/spin.h
index 33e2ab87572..eece7026d3b 100644
--- a/src/include/storage/spin.h
+++ b/src/include/storage/spin.h
@@ -14,9 +14,11 @@
* Acquire a spinlock, waiting if necessary.
* Time out and abort() if unable to acquire the lock in a
* "reasonable" amount of time --- typically ~ 1 minute.
+ * Acquire (including read barrier) semantics.
*
* void SpinLockRelease(volatile slock_t *lock)
* Unlock a previously acquired lock.
+ * Release (including write barrier) semantics.
*
* bool SpinLockFree(slock_t *lock)
* Tests if the lock is free. Returns true if free, false if locked.
@@ -35,11 +37,6 @@
* for a CHECK_FOR_INTERRUPTS() to occur while holding a spinlock, and so
* it is not necessary to do HOLD/RESUME_INTERRUPTS() in these macros.
*
- * These macros are implemented in terms of hardware-dependent macros
- * supplied by s_lock.h. There is not currently any extra functionality
- * added by this header, but there has been in the past and may someday
- * be again.
- *
*
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -51,15 +48,68 @@
#ifndef SPIN_H
#define SPIN_H
-#include "storage/s_lock.h"
+#ifdef FRONTEND
+#error "spin.h may not be included from frontend code"
+#endif
+
+#include "port/atomics.h"
+
+/* Support for dynamic adjustment of spins_per_delay */
+#define DEFAULT_SPINS_PER_DELAY 100
+
+typedef pg_atomic_flag slock_t;
+
+/*
+ * Support for spin delay which is useful in various places where
+ * spinlock-like procedures take place.
+ */
+typedef struct
+{
+ int spins;
+ int delays;
+ int cur_delay;
+ const char *file;
+ int line;
+ const char *func;
+} SpinDelayStatus;
+
+static inline void
+init_spin_delay(SpinDelayStatus *status,
+ const char *file, int line, const char *func)
+{
+ status->spins = 0;
+ status->delays = 0;
+ status->cur_delay = 0;
+ status->file = file;
+ status->line = line;
+ status->func = func;
+}
+#define init_local_spin_delay(status) init_spin_delay(status, __FILE__, __LINE__, __func__)
+extern void perform_spin_delay(SpinDelayStatus *status);
+extern void finish_spin_delay(SpinDelayStatus *status);
+extern void set_spins_per_delay(int shared_spins_per_delay);
+extern int update_spins_per_delay(int shared_spins_per_delay);
-#define SpinLockInit(lock) S_INIT_LOCK(lock)
+/* Out-of-line part of spinlock acquisition. */
+extern int s_lock(volatile slock_t *lock,
+ const char *file, int line,
+ const char *func);
-#define SpinLockAcquire(lock) S_LOCK(lock)
+static inline void
+SpinLockInit(volatile slock_t *lock)
+{
+ pg_atomic_init_flag(lock);
+}
-#define SpinLockRelease(lock) S_UNLOCK(lock)
+#define SpinLockAcquire(lock) \
+ (pg_atomic_test_set_flag(lock) ? 0 : \
+ s_lock((lock), __FILE__, __LINE__, __func__))
-#define SpinLockFree(lock) S_LOCK_FREE(lock)
+static inline void
+SpinLockRelease(volatile slock_t *lock)
+{
+ pg_atomic_clear_flag(lock);
+}
#endif /* SPIN_H */
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index a2db6080876..dd4034e8eda 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -658,32 +658,9 @@ test_spinlock(void)
SpinLockAcquire(&struct_w_lock.lock);
SpinLockRelease(&struct_w_lock.lock);
- /* test basic operations via underlying S_* API */
- S_INIT_LOCK(&struct_w_lock.lock);
- S_LOCK(&struct_w_lock.lock);
- S_UNLOCK(&struct_w_lock.lock);
-
/* and that "contended" acquisition works */
s_lock(&struct_w_lock.lock, "testfile", 17, "testfunc");
- S_UNLOCK(&struct_w_lock.lock);
-
- /*
- * Check, using TAS directly, that a single spin cycle doesn't block
- * when acquiring an already acquired lock.
- */
-#ifdef TAS
- S_LOCK(&struct_w_lock.lock);
-
- if (!TAS(&struct_w_lock.lock))
- elog(ERROR, "acquired already held spinlock");
-
-#ifdef TAS_SPIN
- if (!TAS_SPIN(&struct_w_lock.lock))
- elog(ERROR, "acquired already held spinlock");
-#endif /* defined(TAS_SPIN) */
-
- S_UNLOCK(&struct_w_lock.lock);
-#endif /* defined(TAS) */
+ SpinLockRelease(&struct_w_lock.lock);
/*
* Verify that after all of this the non-lock contents are still
--
2.50.1 (Apple Git-155)
[application/octet-stream] v2-0005-Remove-configure-meson-checks-for-atomics.patch (16.1K, 6-v2-0005-Remove-configure-meson-checks-for-atomics.patch)
download | inline diff:
From 7a55ded353250beb7b77d902813788505b79dbda Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 10 Nov 2025 23:03:00 +1300
Subject: [PATCH v2 5/5] Remove configure/meson checks for atomics.
These are now unreferenced, but this was kept separate from the two
patches that did that to disentangle them.
---
config/c-compiler.m4 | 99 ------------------
configure | 200 ------------------------------------
configure.ac | 10 --
meson.build | 63 ------------
src/backend/Makefile | 2 +-
src/backend/port/.gitignore | 1 -
src/include/pg_config.h.in | 20 ----
7 files changed, 1 insertion(+), 394 deletions(-)
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 236a59e8536..e05570255b3 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -446,105 +446,6 @@ AC_DEFUN([PGAC_PROG_CC_LDFLAGS_OPT],
[PGAC_PROG_CC_LD_VARFLAGS_OPT(LDFLAGS, [$1], [$2])
])# PGAC_PROG_CC_LDFLAGS_OPT
-
-# PGAC_HAVE_GCC__SYNC_CHAR_TAS
-# ----------------------------
-# Check if the C compiler understands __sync_lock_test_and_set(char),
-# and define HAVE_GCC__SYNC_CHAR_TAS
-#
-# NB: There are platforms where test_and_set is available but compare_and_swap
-# is not, so test this separately.
-# NB: Some platforms only do 32bit tas, others only do 8bit tas. Test both.
-AC_DEFUN([PGAC_HAVE_GCC__SYNC_CHAR_TAS],
-[AC_CACHE_CHECK(for builtin __sync char locking functions, pgac_cv_gcc_sync_char_tas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
- [char lock = 0;
- __sync_lock_test_and_set(&lock, 1);
- __sync_lock_release(&lock);])],
- [pgac_cv_gcc_sync_char_tas="yes"],
- [pgac_cv_gcc_sync_char_tas="no"])])
-if test x"$pgac_cv_gcc_sync_char_tas" = x"yes"; then
- AC_DEFINE(HAVE_GCC__SYNC_CHAR_TAS, 1, [Define to 1 if you have __sync_lock_test_and_set(char *) and friends.])
-fi])# PGAC_HAVE_GCC__SYNC_CHAR_TAS
-
-# PGAC_HAVE_GCC__SYNC_INT32_TAS
-# -----------------------------
-# Check if the C compiler understands __sync_lock_test_and_set(),
-# and define HAVE_GCC__SYNC_INT32_TAS
-AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT32_TAS],
-[AC_CACHE_CHECK(for builtin __sync int32 locking functions, pgac_cv_gcc_sync_int32_tas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
- [int lock = 0;
- __sync_lock_test_and_set(&lock, 1);
- __sync_lock_release(&lock);])],
- [pgac_cv_gcc_sync_int32_tas="yes"],
- [pgac_cv_gcc_sync_int32_tas="no"])])
-if test x"$pgac_cv_gcc_sync_int32_tas" = x"yes"; then
- AC_DEFINE(HAVE_GCC__SYNC_INT32_TAS, 1, [Define to 1 if you have __sync_lock_test_and_set(int *) and friends.])
-fi])# PGAC_HAVE_GCC__SYNC_INT32_TAS
-
-# PGAC_HAVE_GCC__SYNC_INT32_CAS
-# -----------------------------
-# Check if the C compiler understands __sync_compare_and_swap() for 32bit
-# types, and define HAVE_GCC__SYNC_INT32_CAS if so.
-AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT32_CAS],
-[AC_CACHE_CHECK(for builtin __sync int32 atomic operations, pgac_cv_gcc_sync_int32_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
- [int val = 0;
- __sync_val_compare_and_swap(&val, 0, 37);])],
- [pgac_cv_gcc_sync_int32_cas="yes"],
- [pgac_cv_gcc_sync_int32_cas="no"])])
-if test x"$pgac_cv_gcc_sync_int32_cas" = x"yes"; then
- AC_DEFINE(HAVE_GCC__SYNC_INT32_CAS, 1, [Define to 1 if you have __sync_val_compare_and_swap(int *, int, int).])
-fi])# PGAC_HAVE_GCC__SYNC_INT32_CAS
-
-# PGAC_HAVE_GCC__SYNC_INT64_CAS
-# -----------------------------
-# Check if the C compiler understands __sync_compare_and_swap() for 64bit
-# types, and define HAVE_GCC__SYNC_INT64_CAS if so.
-AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT64_CAS],
-[AC_CACHE_CHECK(for builtin __sync int64 atomic operations, pgac_cv_gcc_sync_int64_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdint.h>],
- [int64_t lock = 0;
- __sync_val_compare_and_swap(&lock, 0, (int64_t) 37);])],
- [pgac_cv_gcc_sync_int64_cas="yes"],
- [pgac_cv_gcc_sync_int64_cas="no"])])
-if test x"$pgac_cv_gcc_sync_int64_cas" = x"yes"; then
- AC_DEFINE(HAVE_GCC__SYNC_INT64_CAS, 1, [Define to 1 if you have __sync_val_compare_and_swap(int64_t *, int64_t, int64_t).])
-fi])# PGAC_HAVE_GCC__SYNC_INT64_CAS
-
-# PGAC_HAVE_GCC__ATOMIC_INT32_CAS
-# -------------------------------
-# Check if the C compiler understands __atomic_compare_exchange_n() for 32bit
-# types, and define HAVE_GCC__ATOMIC_INT32_CAS if so.
-AC_DEFUN([PGAC_HAVE_GCC__ATOMIC_INT32_CAS],
-[AC_CACHE_CHECK(for builtin __atomic int32 atomic operations, pgac_cv_gcc_atomic_int32_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
- [int val = 0;
- int expect = 0;
- __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);])],
- [pgac_cv_gcc_atomic_int32_cas="yes"],
- [pgac_cv_gcc_atomic_int32_cas="no"])])
-if test x"$pgac_cv_gcc_atomic_int32_cas" = x"yes"; then
- AC_DEFINE(HAVE_GCC__ATOMIC_INT32_CAS, 1, [Define to 1 if you have __atomic_compare_exchange_n(int *, int *, int).])
-fi])# PGAC_HAVE_GCC__ATOMIC_INT32_CAS
-
-# PGAC_HAVE_GCC__ATOMIC_INT64_CAS
-# -------------------------------
-# Check if the C compiler understands __atomic_compare_exchange_n() for 64bit
-# types, and define HAVE_GCC__ATOMIC_INT64_CAS if so.
-AC_DEFUN([PGAC_HAVE_GCC__ATOMIC_INT64_CAS],
-[AC_CACHE_CHECK(for builtin __atomic int64 atomic operations, pgac_cv_gcc_atomic_int64_cas,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdint.h>],
- [int64_t val = 0;
- int64_t expect = 0;
- __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);])],
- [pgac_cv_gcc_atomic_int64_cas="yes"],
- [pgac_cv_gcc_atomic_int64_cas="no"])])
-if test x"$pgac_cv_gcc_atomic_int64_cas" = x"yes"; then
- AC_DEFINE(HAVE_GCC__ATOMIC_INT64_CAS, 1, [Define to 1 if you have __atomic_compare_exchange_n(int64 *, int64 *, int64).])
-fi])# PGAC_HAVE_GCC__ATOMIC_INT64_CAS
-
# PGAC_SSE42_CRC32_INTRINSICS
# ---------------------------
# Check if the compiler supports the x86 CRC instructions added in SSE 4.2,
diff --git a/configure b/configure
index 3a0ed11fa8e..bd7dee3fdf3 100755
--- a/configure
+++ b/configure
@@ -17167,206 +17167,6 @@ _ACEOF
fi
fi
-# Check for various atomic operations now that we have checked how to declare
-# 64bit integers.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __sync char locking functions" >&5
-$as_echo_n "checking for builtin __sync char locking functions... " >&6; }
-if ${pgac_cv_gcc_sync_char_tas+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-char lock = 0;
- __sync_lock_test_and_set(&lock, 1);
- __sync_lock_release(&lock);
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- pgac_cv_gcc_sync_char_tas="yes"
-else
- pgac_cv_gcc_sync_char_tas="no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_sync_char_tas" >&5
-$as_echo "$pgac_cv_gcc_sync_char_tas" >&6; }
-if test x"$pgac_cv_gcc_sync_char_tas" = x"yes"; then
-
-$as_echo "#define HAVE_GCC__SYNC_CHAR_TAS 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __sync int32 locking functions" >&5
-$as_echo_n "checking for builtin __sync int32 locking functions... " >&6; }
-if ${pgac_cv_gcc_sync_int32_tas+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-int lock = 0;
- __sync_lock_test_and_set(&lock, 1);
- __sync_lock_release(&lock);
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- pgac_cv_gcc_sync_int32_tas="yes"
-else
- pgac_cv_gcc_sync_int32_tas="no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_sync_int32_tas" >&5
-$as_echo "$pgac_cv_gcc_sync_int32_tas" >&6; }
-if test x"$pgac_cv_gcc_sync_int32_tas" = x"yes"; then
-
-$as_echo "#define HAVE_GCC__SYNC_INT32_TAS 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __sync int32 atomic operations" >&5
-$as_echo_n "checking for builtin __sync int32 atomic operations... " >&6; }
-if ${pgac_cv_gcc_sync_int32_cas+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-int val = 0;
- __sync_val_compare_and_swap(&val, 0, 37);
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- pgac_cv_gcc_sync_int32_cas="yes"
-else
- pgac_cv_gcc_sync_int32_cas="no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_sync_int32_cas" >&5
-$as_echo "$pgac_cv_gcc_sync_int32_cas" >&6; }
-if test x"$pgac_cv_gcc_sync_int32_cas" = x"yes"; then
-
-$as_echo "#define HAVE_GCC__SYNC_INT32_CAS 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __sync int64 atomic operations" >&5
-$as_echo_n "checking for builtin __sync int64 atomic operations... " >&6; }
-if ${pgac_cv_gcc_sync_int64_cas+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdint.h>
-int
-main ()
-{
-int64_t lock = 0;
- __sync_val_compare_and_swap(&lock, 0, (int64_t) 37);
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- pgac_cv_gcc_sync_int64_cas="yes"
-else
- pgac_cv_gcc_sync_int64_cas="no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_sync_int64_cas" >&5
-$as_echo "$pgac_cv_gcc_sync_int64_cas" >&6; }
-if test x"$pgac_cv_gcc_sync_int64_cas" = x"yes"; then
-
-$as_echo "#define HAVE_GCC__SYNC_INT64_CAS 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __atomic int32 atomic operations" >&5
-$as_echo_n "checking for builtin __atomic int32 atomic operations... " >&6; }
-if ${pgac_cv_gcc_atomic_int32_cas+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-int val = 0;
- int expect = 0;
- __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- pgac_cv_gcc_atomic_int32_cas="yes"
-else
- pgac_cv_gcc_atomic_int32_cas="no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_atomic_int32_cas" >&5
-$as_echo "$pgac_cv_gcc_atomic_int32_cas" >&6; }
-if test x"$pgac_cv_gcc_atomic_int32_cas" = x"yes"; then
-
-$as_echo "#define HAVE_GCC__ATOMIC_INT32_CAS 1" >>confdefs.h
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __atomic int64 atomic operations" >&5
-$as_echo_n "checking for builtin __atomic int64 atomic operations... " >&6; }
-if ${pgac_cv_gcc_atomic_int64_cas+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdint.h>
-int
-main ()
-{
-int64_t val = 0;
- int64_t expect = 0;
- __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- pgac_cv_gcc_atomic_int64_cas="yes"
-else
- pgac_cv_gcc_atomic_int64_cas="no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_gcc_atomic_int64_cas" >&5
-$as_echo "$pgac_cv_gcc_atomic_int64_cas" >&6; }
-if test x"$pgac_cv_gcc_atomic_int64_cas" = x"yes"; then
-
-$as_echo "#define HAVE_GCC__ATOMIC_INT64_CAS 1" >>confdefs.h
-
-fi
-
-
# Check for __get_cpuid() and __cpuid()
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __get_cpuid" >&5
$as_echo_n "checking for __get_cpuid... " >&6; }
diff --git a/configure.ac b/configure.ac
index c2413720a18..e3b75601df7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2019,16 +2019,6 @@ AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignme
# Some compilers offer a 128-bit integer scalar type.
PGAC_TYPE_128BIT_INT
-# Check for various atomic operations now that we have checked how to declare
-# 64bit integers.
-PGAC_HAVE_GCC__SYNC_CHAR_TAS
-PGAC_HAVE_GCC__SYNC_INT32_TAS
-PGAC_HAVE_GCC__SYNC_INT32_CAS
-PGAC_HAVE_GCC__SYNC_INT64_CAS
-PGAC_HAVE_GCC__ATOMIC_INT32_CAS
-PGAC_HAVE_GCC__ATOMIC_INT64_CAS
-
-
# Check for __get_cpuid() and __cpuid()
AC_CACHE_CHECK([for __get_cpuid], [pgac_cv__get_cpuid],
[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <cpuid.h>],
diff --git a/meson.build b/meson.build
index 04c563378c7..0fb783fcb64 100644
--- a/meson.build
+++ b/meson.build
@@ -2224,69 +2224,6 @@ if llvm.found()
endif
-
-###############################################################
-# Atomics
-###############################################################
-
-atomic_checks = [
- {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
- 'desc': '__sync_lock_test_and_set(char)',
- 'test': '''
-char lock = 0;
-__sync_lock_test_and_set(&lock, 1);
-__sync_lock_release(&lock);'''},
-
- {'name': 'HAVE_GCC__SYNC_INT32_TAS',
- 'desc': '__sync_lock_test_and_set(int32)',
- 'test': '''
-int lock = 0;
-__sync_lock_test_and_set(&lock, 1);
-__sync_lock_release(&lock);'''},
-
- {'name': 'HAVE_GCC__SYNC_INT32_CAS',
- 'desc': '__sync_val_compare_and_swap(int32)',
- 'test': '''
-int val = 0;
-__sync_val_compare_and_swap(&val, 0, 37);'''},
-
- {'name': 'HAVE_GCC__SYNC_INT64_CAS',
- 'desc': '__sync_val_compare_and_swap(int64)',
- 'test': '''
-int64_t val = 0;
-__sync_val_compare_and_swap(&val, 0, 37);'''},
-
- {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
- 'desc': ' __atomic_compare_exchange_n(int32)',
- 'test': '''
-int val = 0;
-int expect = 0;
-__atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
-
- {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
- 'desc': ' __atomic_compare_exchange_n(int64)',
- 'test': '''
-int64_t val = 0;
-int64_t expect = 0;
-__atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
-]
-
-foreach check : atomic_checks
- test = '''
-#include <stdint.h>
-int main(void)
-{
-@0@
-}'''.format(check['test'])
-
- cdata.set(check['name'],
- cc.links(test,
- name: check['desc'],
- args: test_c_args) ? 1 : false
- )
-endforeach
-
-
###############################################################
# Check for the availability of XSAVE intrinsics.
###############################################################
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 7344c8c7f5c..b0d97dc2fa2 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -265,7 +265,7 @@ endif
distclean: clean
# generated by configure
- rm -f port/tas.s port/pg_sema.c port/pg_shmem.c
+ rm -f port/pg_sema.c port/pg_shmem.c
##########################################################################
diff --git a/src/backend/port/.gitignore b/src/backend/port/.gitignore
index 4ef36b82c77..6c5067a4a9f 100644
--- a/src/backend/port/.gitignore
+++ b/src/backend/port/.gitignore
@@ -1,3 +1,2 @@
/pg_sema.c
/pg_shmem.c
-/tas.s
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..ad5663a8915 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -149,26 +149,6 @@
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
#undef HAVE_FSEEKO
-/* Define to 1 if you have __atomic_compare_exchange_n(int *, int *, int). */
-#undef HAVE_GCC__ATOMIC_INT32_CAS
-
-/* Define to 1 if you have __atomic_compare_exchange_n(int64 *, int64 *,
- int64). */
-#undef HAVE_GCC__ATOMIC_INT64_CAS
-
-/* Define to 1 if you have __sync_lock_test_and_set(char *) and friends. */
-#undef HAVE_GCC__SYNC_CHAR_TAS
-
-/* Define to 1 if you have __sync_val_compare_and_swap(int *, int, int). */
-#undef HAVE_GCC__SYNC_INT32_CAS
-
-/* Define to 1 if you have __sync_lock_test_and_set(int *) and friends. */
-#undef HAVE_GCC__SYNC_INT32_TAS
-
-/* Define to 1 if you have __sync_val_compare_and_swap(int64_t *, int64_t,
- int64_t). */
-#undef HAVE_GCC__SYNC_INT64_CAS
-
/* Define to 1 if you have the `getauxval' function. */
#undef HAVE_GETAUXVAL
--
2.50.1 (Apple Git-155)
view thread (19+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: Trying out <stdatomic.h>
In-Reply-To: <CA+hUKG+bub1T9TFLUAJHr+Z519VRgJS5tb10xBxEtJZRH8GXiA@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox