Commit e8fdbd58 authored by Andres Freund's avatar Andres Freund

Improve 64bit atomics support.

When adding atomics back in b64d92f1, I added 64bit support as
optional; there wasn't yet a direct user in sight.  That turned out to
be a bit short-sighted, it'd already have been useful a number of times.

Add a fallback implementation of 64bit atomics, just like the one we
have for 32bit atomics.

Additionally optimize reads/writes to 64bit on a number of platforms
where aligned writes of that size are atomic. This can now be tested
with PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY.

Author: Andres Freund
Reviewed-By: Amit Kapila
Discussion: https://postgr.es/m/20160330230914.GH13305@awork2.anarazel.de
parent 28afad5c
...@@ -89,7 +89,7 @@ void ...@@ -89,7 +89,7 @@ void
pg_atomic_init_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val_) pg_atomic_init_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val_)
{ {
StaticAssertStmt(sizeof(ptr->sema) >= sizeof(slock_t), StaticAssertStmt(sizeof(ptr->sema) >= sizeof(slock_t),
"size mismatch of atomic_flag vs slock_t"); "size mismatch of atomic_uint32 vs slock_t");
/* /*
* If we're using semaphore based atomic flags, be careful about nested * If we're using semaphore based atomic flags, be careful about nested
...@@ -157,3 +157,66 @@ pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) ...@@ -157,3 +157,66 @@ pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_)
} }
#endif /* PG_HAVE_ATOMIC_U32_SIMULATION */ #endif /* PG_HAVE_ATOMIC_U32_SIMULATION */
#ifdef PG_HAVE_ATOMIC_U64_SIMULATION
void
pg_atomic_init_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val_)
{
StaticAssertStmt(sizeof(ptr->sema) >= sizeof(slock_t),
"size mismatch of atomic_uint64 vs slock_t");
/*
* If we're using semaphore based atomic flags, be careful about nested
* usage of atomics while a spinlock is held.
*/
#ifndef HAVE_SPINLOCKS
s_init_lock_sema((slock_t *) &ptr->sema, true);
#else
SpinLockInit((slock_t *) &ptr->sema);
#endif
ptr->value = val_;
}
bool
pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr,
uint64 *expected, uint64 newval)
{
bool ret;
/*
* Do atomic op under a spinlock. It might look like we could just skip
* the cmpxchg if the lock isn't available, but that'd just emulate a
* 'weak' compare and swap. I.e. one that allows spurious failures. Since
* several algorithms rely on a strong variant and that is efficiently
* implementable on most major architectures let's emulate it here as
* well.
*/
SpinLockAcquire((slock_t *) &ptr->sema);
/* perform compare/exchange logic */
ret = ptr->value == *expected;
*expected = ptr->value;
if (ret)
ptr->value = newval;
/* and release lock */
SpinLockRelease((slock_t *) &ptr->sema);
return ret;
}
uint64
pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_)
{
uint64 oldval;
SpinLockAcquire((slock_t *) &ptr->sema);
oldval = ptr->value;
ptr->value += add_;
SpinLockRelease((slock_t *) &ptr->sema);
return oldval;
}
#endif /* PG_HAVE_ATOMIC_U64_SIMULATION */
...@@ -12,13 +12,14 @@ ...@@ -12,13 +12,14 @@
* * pg_compiler_barrier(), pg_write_barrier(), pg_read_barrier() * * pg_compiler_barrier(), pg_write_barrier(), pg_read_barrier()
* * pg_atomic_compare_exchange_u32(), pg_atomic_fetch_add_u32() * * pg_atomic_compare_exchange_u32(), pg_atomic_fetch_add_u32()
* * pg_atomic_test_set_flag(), pg_atomic_init_flag(), pg_atomic_clear_flag() * * 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 * There exist generic, hardware independent, implementations for several
* compilers which might be sufficient, although possibly not optimal, for a * compilers which might be sufficient, although possibly not optimal, for a
* new platform. If no such generic implementation is available spinlocks (or * new platform. If no such generic implementation is available spinlocks (or
* even OS provided semaphores) will be used to implement the API. * even OS provided semaphores) will be used to implement the API.
* *
* Implement the _u64 variants if and only if your platform can use them * Implement _u64 atomics if and only if your platform can use them
* efficiently (and obviously correctly). * efficiently (and obviously correctly).
* *
* Use higher level functionality (lwlocks, spinlocks, heavyweight locks) * Use higher level functionality (lwlocks, spinlocks, heavyweight locks)
...@@ -110,9 +111,9 @@ ...@@ -110,9 +111,9 @@
/* /*
* Provide a full fallback of the pg_*_barrier(), pg_atomic**_flag and * Provide a full fallback of the pg_*_barrier(), pg_atomic**_flag and
* pg_atomic_*_u32 APIs for platforms without sufficient spinlock and/or * pg_atomic_* APIs for platforms without sufficient spinlock and/or atomics
* atomics support. In the case of spinlock backed atomics the emulation is * support. In the case of spinlock backed atomics the emulation is expected
* expected to be efficient, although less so than native atomics support. * to be efficient, although less so than native atomics support.
*/ */
#include "port/atomics/fallback.h" #include "port/atomics/fallback.h"
...@@ -421,8 +422,6 @@ pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_) ...@@ -421,8 +422,6 @@ pg_atomic_sub_fetch_u32(volatile pg_atomic_uint32 *ptr, int32 sub_)
* documentation. * documentation.
* ---- * ----
*/ */
#ifdef PG_HAVE_ATOMIC_U64_SUPPORT
static inline void static inline void
pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val) pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
{ {
...@@ -506,8 +505,6 @@ pg_atomic_sub_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 sub_) ...@@ -506,8 +505,6 @@ pg_atomic_sub_fetch_u64(volatile pg_atomic_uint64 *ptr, int64 sub_)
return pg_atomic_sub_fetch_u64_impl(ptr, sub_); return pg_atomic_sub_fetch_u64_impl(ptr, sub_);
} }
#endif /* PG_HAVE_64_BIT_ATOMICS */
#undef INSIDE_ATOMICS_H #undef INSIDE_ATOMICS_H
#endif /* ATOMICS_H */ #endif /* ATOMICS_H */
...@@ -24,3 +24,6 @@ ...@@ -24,3 +24,6 @@
#elif defined(__hpux) #elif defined(__hpux)
# define pg_memory_barrier_impl() _Asm_mf() # define pg_memory_barrier_impl() _Asm_mf()
#endif #endif
/* per architecture manual doubleword accesses have single copy atomicity */
#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY
...@@ -24,3 +24,6 @@ ...@@ -24,3 +24,6 @@
#define pg_read_barrier_impl() __asm__ __volatile__ ("lwsync" : : : "memory") #define pg_read_barrier_impl() __asm__ __volatile__ ("lwsync" : : : "memory")
#define pg_write_barrier_impl() __asm__ __volatile__ ("lwsync" : : : "memory") #define pg_write_barrier_impl() __asm__ __volatile__ ("lwsync" : : : "memory")
#endif #endif
/* per architecture manual doubleword accesses have single copy atomicity */
#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY
...@@ -239,4 +239,14 @@ pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) ...@@ -239,4 +239,14 @@ pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_)
#endif /* defined(__GNUC__) || defined(__INTEL_COMPILER) */ #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, sunpro, msvc */
#define PG_HAVE_8BYTE_SINGLE_COPY_ATOMICITY
#endif /* 8 byte single-copy atomicity */
#endif /* HAVE_ATOMICS */ #endif /* HAVE_ATOMICS */
...@@ -102,6 +102,24 @@ typedef struct pg_atomic_uint32 ...@@ -102,6 +102,24 @@ typedef struct pg_atomic_uint32
#endif /* PG_HAVE_ATOMIC_U32_SUPPORT */ #endif /* PG_HAVE_ATOMIC_U32_SUPPORT */
#if !defined(PG_HAVE_ATOMIC_U64_SUPPORT)
#define PG_HAVE_ATOMIC_U64_SIMULATION
#define PG_HAVE_ATOMIC_U64_SUPPORT
typedef struct pg_atomic_uint64
{
/* Check pg_atomic_flag's definition above for an explanation */
#if defined(__hppa) || defined(__hppa__) /* HP PA-RISC, GCC and HP compilers */
int sema[4];
#else
int sema;
#endif
volatile uint64 value;
} pg_atomic_uint64;
#endif /* PG_HAVE_ATOMIC_U64_SUPPORT */
#ifdef PG_HAVE_ATOMIC_FLAG_SIMULATION #ifdef PG_HAVE_ATOMIC_FLAG_SIMULATION
#define PG_HAVE_ATOMIC_INIT_FLAG #define PG_HAVE_ATOMIC_INIT_FLAG
...@@ -144,3 +162,18 @@ extern bool pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, ...@@ -144,3 +162,18 @@ extern bool pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr,
extern uint32 pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_); extern uint32 pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_);
#endif /* PG_HAVE_ATOMIC_U32_SIMULATION */ #endif /* PG_HAVE_ATOMIC_U32_SIMULATION */
#ifdef PG_HAVE_ATOMIC_U64_SIMULATION
#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_SIMULATION */
...@@ -255,8 +255,6 @@ pg_atomic_sub_fetch_u32_impl(volatile pg_atomic_uint32 *ptr, int32 sub_) ...@@ -255,8 +255,6 @@ pg_atomic_sub_fetch_u32_impl(volatile pg_atomic_uint32 *ptr, int32 sub_)
} }
#endif #endif
#ifdef PG_HAVE_ATOMIC_U64_SUPPORT
#if !defined(PG_HAVE_ATOMIC_EXCHANGE_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) #if !defined(PG_HAVE_ATOMIC_EXCHANGE_U64) && defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64)
#define PG_HAVE_ATOMIC_EXCHANGE_U64 #define PG_HAVE_ATOMIC_EXCHANGE_U64
static inline uint64 static inline uint64
...@@ -273,6 +271,24 @@ pg_atomic_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 xchg_) ...@@ -273,6 +271,24 @@ pg_atomic_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 xchg_)
} }
#endif #endif
#ifndef PG_HAVE_ATOMIC_READ_U64
#define PG_HAVE_ATOMIC_READ_U64
static inline uint64
pg_atomic_read_u64_impl(volatile pg_atomic_uint64 *ptr)
{
return *(&ptr->value);
}
#endif
#ifndef PG_HAVE_ATOMIC_WRITE_U64
#define PG_HAVE_ATOMIC_WRITE_U64
static inline void
pg_atomic_write_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 val)
{
ptr->value = val;
}
#endif
#ifndef PG_HAVE_ATOMIC_WRITE_U64 #ifndef PG_HAVE_ATOMIC_WRITE_U64
#define PG_HAVE_ATOMIC_WRITE_U64 #define PG_HAVE_ATOMIC_WRITE_U64
static inline void static inline void
...@@ -388,5 +404,3 @@ pg_atomic_sub_fetch_u64_impl(volatile pg_atomic_uint64 *ptr, int64 sub_) ...@@ -388,5 +404,3 @@ pg_atomic_sub_fetch_u64_impl(volatile pg_atomic_uint64 *ptr, int64 sub_)
return pg_atomic_fetch_sub_u64_impl(ptr, sub_) - sub_; return pg_atomic_fetch_sub_u64_impl(ptr, sub_) - sub_;
} }
#endif #endif
#endif /* PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 */
...@@ -997,7 +997,6 @@ test_atomic_uint32(void) ...@@ -997,7 +997,6 @@ test_atomic_uint32(void)
elog(ERROR, "pg_atomic_fetch_and_u32() #3 wrong"); elog(ERROR, "pg_atomic_fetch_and_u32() #3 wrong");
} }
#ifdef PG_HAVE_ATOMIC_U64_SUPPORT
static void static void
test_atomic_uint64(void) test_atomic_uint64(void)
{ {
...@@ -1073,7 +1072,6 @@ test_atomic_uint64(void) ...@@ -1073,7 +1072,6 @@ test_atomic_uint64(void)
if (pg_atomic_fetch_and_u64(&var, ~0) != 0) if (pg_atomic_fetch_and_u64(&var, ~0) != 0)
elog(ERROR, "pg_atomic_fetch_and_u64() #3 wrong"); elog(ERROR, "pg_atomic_fetch_and_u64() #3 wrong");
} }
#endif /* PG_HAVE_ATOMIC_U64_SUPPORT */
PG_FUNCTION_INFO_V1(test_atomic_ops); PG_FUNCTION_INFO_V1(test_atomic_ops);
...@@ -1096,9 +1094,7 @@ test_atomic_ops(PG_FUNCTION_ARGS) ...@@ -1096,9 +1094,7 @@ test_atomic_ops(PG_FUNCTION_ARGS)
test_atomic_uint32(); test_atomic_uint32();
#ifdef PG_HAVE_ATOMIC_U64_SUPPORT
test_atomic_uint64(); test_atomic_uint64();
#endif
PG_RETURN_BOOL(true); PG_RETURN_BOOL(true);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment