123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- /*-------------------------------------------------------------------------
- *
- * 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.
- *
- * 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()
- *
- * 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 (or
- * even OS provided semaphores) will be used to implement the API.
- *
- * Implement the _u64 variants if and only if your platform can use them
- * efficiently (and obviously correctly).
- *
- * Use higher level functionality (lwlocks, spinlocks, heavyweight locks)
- * whenever possible. Writing correct code using these facilities is hard.
- *
- * For an introduction to using memory barriers within the PostgreSQL backend,
- * see src/backend/storage/lmgr/README.barrier
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/include/port/atomics.h
- *
- *-------------------------------------------------------------------------
- */
- #ifndef ATOMICS_H
- #define ATOMICS_H
- #ifdef FRONTEND
- #error "atomics.h may not be included from frontend code"
- #endif
- #define INSIDE_ATOMICS_H
- #include <limits.h>
- /*
- * 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__) || defined(__aarch64)
- #include "port/atomics/arch-arm.h"
- #elif defined(__i386__) || defined(__i386) || defined(__x86_64__)
- #include "port/atomics/arch-x86.h"
- #elif defined(__ia64__) || defined(__ia64)
- #include "port/atomics/arch-ia64.h"
- #elif defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__)
- #include "port/atomics/arch-ppc.h"
- #elif defined(__hppa) || defined(__hppa__)
- #include "port/atomics/arch-hppa.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.
- */
- /*
- * Given a gcc-compatible xlc compiler, prefer the xlc implementation. The
- * ppc64le "IBM XL C/C++ for Linux, V13.1.2" implements both interfaces, but
- * __sync_lock_test_and_set() of one-byte types elicits SIGSEGV.
- */
- #if defined(__IBMC__) || defined(__IBMCPP__)
- #include "port/atomics/generic-xlc.h"
- /* gcc or compatible, including clang and icc */
- #elif defined(__GNUC__) || defined(__INTEL_COMPILER)
- #include "port/atomics/generic-gcc.h"
- #elif defined(WIN32_ONLY_COMPILER)
- #include "port/atomics/generic-msvc.h"
- #elif defined(__hpux) && defined(__ia64) && !defined(__GNUC__)
- #include "port/atomics/generic-acc.h"
- #elif defined(__SUNPRO_C) && !defined(__GNUC__)
- #include "port/atomics/generic-sunpro.h"
- #else
- /*
- * Unsupported compiler, we'll likely use slower fallbacks... At least
- * compiler barriers should really be provided.
- */
- #endif
- /*
- * Provide a full fallback of the pg_*_barrier(), pg_atomic**_flag and
- * pg_atomic_*_u32 APIs for platforms without sufficient spinlock and/or
- * atomics support. In the case of spinlock backed atomics the emulation is
- * expected to be efficient, although less so than native atomics support.
- */
- #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
- *
- * A compiler barrier need not (and preferably should not) emit any actual
- * machine code, but must act as an optimization fence: the compiler must not
- * 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.
- */
- #define pg_compiler_barrier() pg_compiler_barrier_impl()
- /*
- * pg_memory_barrier - prevent the CPU from reordering memory access
- *
- * A memory barrier must act as a compiler barrier, and in addition must
- * guarantee that all loads and stores issued prior to the barrier are
- * completed before any loads or stores issued after the barrier. Unless
- * loads and stores are totally ordered (which is not the case on most
- * architectures) this requires issuing some sort of memory fencing
- * instruction.
- */
- #define pg_memory_barrier() pg_memory_barrier_impl()
- /*
- * pg_(read|write)_barrier - prevent the CPU from reordering memory access
- *
- * A read barrier must act as a compiler barrier, and in addition must
- * guarantee that any loads issued prior to the barrier are completed before
- * any loads issued after the barrier. Similarly, a write barrier acts
- * as a compiler barrier, and also orders stores. Read and write barriers
- * 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.
- */
- static inline void
- pg_atomic_init_flag(volatile pg_atomic_flag *ptr)
- {
- AssertPointerAlignment(ptr, sizeof(*ptr));
- pg_atomic_init_flag_impl(ptr);
- }
- /*
- * pg_atomic_test_and_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)
- {
- AssertPointerAlignment(ptr, sizeof(*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)
- {
- AssertPointerAlignment(ptr, sizeof(*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)
- {
- AssertPointerAlignment(ptr, sizeof(*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)
- {
- AssertPointerAlignment(ptr, 4);
- return pg_atomic_read_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);
- }
- /*
- * 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);
- }
- /*
- * pg_atomic_fetch_add_u32 - atomically add to variable
- *
- * Returns the value of ptr before the arithmetic operation.
- *
- * Full barrier semantics.
- */
- 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_);
- }
- /*
- * 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.
- */
- 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_);
- }
- /*
- * pg_atomic_fetch_and_u32 - atomically bit-and and_ with variable
- *
- * Returns the value of ptr before the arithmetic operation.
- *
- * 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_);
- }
- /*
- * pg_atomic_fetch_or_u32 - atomically bit-or or_ with variable
- *
- * Returns the value of ptr before the arithmetic operation.
- *
- * Full barrier semantics.
- */
- 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.
- */
- 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.
- * ----
- */
- #ifdef PG_HAVE_ATOMIC_U64_SUPPORT
- static inline void
- pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
- {
- AssertPointerAlignment(ptr, 8);
- pg_atomic_init_u64_impl(ptr, val);
- }
- static inline uint64
- pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
- {
- AssertPointerAlignment(ptr, 8);
- return pg_atomic_read_u64_impl(ptr);
- }
- static inline void
- pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
- {
- AssertPointerAlignment(ptr, 8);
- pg_atomic_write_u64_impl(ptr, val);
- }
- static inline uint64
- pg_atomic_exchange_u64(volatile pg_atomic_uint64 *ptr, uint64 newval)
- {
- AssertPointerAlignment(ptr, 8);
- 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)
- {
- AssertPointerAlignment(ptr, 8);
- AssertPointerAlignment(expected, 8);
- 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_)
- {
- AssertPointerAlignment(ptr, 8);
- return pg_atomic_fetch_add_u64_impl(ptr, add_);
- }
- static inline uint64
- pg_atomic_fetch_sub_u64(volatile pg_atomic_uint64 *ptr, int64 sub_)
- {
- AssertPointerAlignment(ptr, 8);
- 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_)
- {
- AssertPointerAlignment(ptr, 8);
- return pg_atomic_fetch_and_u64_impl(ptr, and_);
- }
- static inline uint64
- pg_atomic_fetch_or_u64(volatile pg_atomic_uint64 *ptr, uint64 or_)
- {
- AssertPointerAlignment(ptr, 8);
- return pg_atomic_fetch_or_u64_impl(ptr, or_);
- }
- static inline uint64
- pg_atomic_add_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 add_)
- {
- AssertPointerAlignment(ptr, 8);
- return pg_atomic_add_fetch_u64_impl(ptr, add_);
- }
- static inline uint64
- pg_atomic_sub_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 sub_)
- {
- AssertPointerAlignment(ptr, 8);
- Assert(sub_ != PG_INT64_MIN);
- return pg_atomic_sub_fetch_u64_impl(ptr, sub_);
- }
- #endif /* PG_HAVE_64_BIT_ATOMICS */
- #undef INSIDE_ATOMICS_H
- #endif /* ATOMICS_H */
|