123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- /*-------------------------------------------------------------------------
- *
- * fallback.h
- * Fallback for platforms without spinlock and/or atomics support. Slower
- * than native atomics support, but not unusably slow.
- *
- * Portions Copyright (c) 1996-2016, 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
- #ifndef pg_memory_barrier_impl
- /*
- * If we have no memory barrier implementation for this architecture, we
- * fall back to acquiring and releasing a spinlock. This might, in turn,
- * fall back to the semaphore-based spinlock implementation, which will be
- * amazingly slow.
- *
- * It's not self-evident that every possible legal implementation of a
- * spinlock acquire-and-release would be equivalent to a full memory barrier.
- * For example, I'm not sure that Itanium's acq and rel add up to a full
- * fence. But all of our actual implementations seem OK in this regard.
- */
- #define PG_HAVE_MEMORY_BARRIER_EMULATION
- extern void pg_spinlock_barrier(void);
- #define pg_memory_barrier_impl pg_spinlock_barrier
- #endif
- #ifndef pg_compiler_barrier_impl
- /*
- * If the compiler/arch combination does not provide compiler barriers,
- * provide a fallback. The fallback simply consists of a function call into
- * an externally defined function. That should guarantee compiler barrier
- * semantics except for compilers that do inter translation unit/global
- * optimization - those better provide an actual compiler barrier.
- *
- * A native compiler barrier for sure is a lot faster than this...
- */
- #define PG_HAVE_COMPILER_BARRIER_EMULATION
- extern void pg_extern_compiler_barrier(void);
- #define pg_compiler_barrier_impl pg_extern_compiler_barrier
- #endif
- /*
- * If we have atomics implementation for this platform, fall back to providing
- * the atomics API using a spinlock to protect the internal state. Possibly
- * the spinlock implementation uses semaphores internally...
- *
- * We have to be a bit careful here, as it's not guaranteed that atomic
- * variables are mapped to the same address in every process (e.g. dynamic
- * shared memory segments). We can't just hash the address and use that to map
- * to a spinlock. Instead assign a spinlock on initialization of the atomic
- * variable.
- */
- #if !defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) && !defined(PG_HAVE_ATOMIC_U32_SUPPORT)
- #define PG_HAVE_ATOMIC_FLAG_SIMULATION
- #define PG_HAVE_ATOMIC_FLAG_SUPPORT
- typedef struct pg_atomic_flag
- {
- /*
- * To avoid circular includes we can't use s_lock as a type here. Instead
- * just reserve enough space for all spinlock types. Some platforms would
- * be content with just one byte instead of 4, but that's not too much
- * waste.
- */
- #if defined(__hppa) || defined(__hppa__) /* HP PA-RISC, GCC and HP compilers */
- int sema[4];
- #else
- int sema;
- #endif
- } pg_atomic_flag;
- #endif /* PG_HAVE_ATOMIC_FLAG_SUPPORT */
- #if !defined(PG_HAVE_ATOMIC_U32_SUPPORT)
- #define PG_HAVE_ATOMIC_U32_SIMULATION
- #define PG_HAVE_ATOMIC_U32_SUPPORT
- typedef struct pg_atomic_uint32
- {
- /* 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 uint32 value;
- } pg_atomic_uint32;
- #endif /* PG_HAVE_ATOMIC_U32_SUPPORT */
- #ifdef PG_HAVE_ATOMIC_FLAG_SIMULATION
- #define PG_HAVE_ATOMIC_INIT_FLAG
- extern void pg_atomic_init_flag_impl(volatile pg_atomic_flag *ptr);
- #define PG_HAVE_ATOMIC_TEST_SET_FLAG
- extern bool pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr);
- #define PG_HAVE_ATOMIC_CLEAR_FLAG
- extern void pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr);
- #define PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG
- static inline bool
- pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *ptr)
- {
- /*
- * Can't do this efficiently in the semaphore based implementation - we'd
- * have to try to acquire the semaphore - so always return true. That's
- * correct, because this is only an unlocked test anyway. Do this in the
- * header so compilers can optimize the test away.
- */
- return true;
- }
- #endif /* PG_HAVE_ATOMIC_FLAG_SIMULATION */
- #ifdef PG_HAVE_ATOMIC_U32_SIMULATION
- #define PG_HAVE_ATOMIC_INIT_U32
- extern void pg_atomic_init_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val_);
- #define PG_HAVE_ATOMIC_WRITE_U32
- extern void pg_atomic_write_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 val);
- #define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32
- extern bool pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr,
- uint32 *expected, uint32 newval);
- #define PG_HAVE_ATOMIC_FETCH_ADD_U32
- extern uint32 pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_);
- #endif /* PG_HAVE_ATOMIC_U32_SIMULATION */
|