postgresql/src/include/port/atomics.h

525 lines
15 KiB
C

/*-------------------------------------------------------------------------
*
* 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()
* * PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY should be defined if appropriate.
*
* 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 _u64 atomics 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-2019, 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.
*/
/*
* gcc or compatible, including clang and icc. Exclude xlc. The ppc64le "IBM
* XL C/C++ for Linux, V13.1.2" emulates gcc, but __sync_lock_test_and_set()
* of one-byte types elicits SIGSEGV. That bug was gone by V13.1.5 (2016-12).
*/
#if (defined(__GNUC__) || defined(__INTEL_COMPILER)) && !(defined(__IBMC__) || defined(__IBMCPP__))
#include "port/atomics/generic-gcc.h"
#elif defined(_MSC_VER)
#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_* 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)
{
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)
{
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.
* ----
*/
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 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 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);
AssertPointerAlignment(expected, 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_);
}
#undef INSIDE_ATOMICS_H
#endif /* ATOMICS_H */