// File: crn_atomics.h #ifndef CRN_ATOMICS_H #define CRN_ATOMICS_H #ifdef WIN32 #pragma once #endif #ifdef WIN32 #include "crn_winhdr.h" #endif #if defined(__GNUC__) && CRNLIB_PLATFORM_PC extern __inline__ __attribute__((__always_inline__,__gnu_inline__)) void crnlib_yield_processor() { __asm__ __volatile__("pause"); } #else CRNLIB_FORCE_INLINE void crnlib_yield_processor() { #if CRNLIB_USE_MSVC_INTRINSICS #if CRNLIB_PLATFORM_PC_X64 _mm_pause(); #else YieldProcessor(); #endif #else // No implementation #endif } #endif #if CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS extern "C" __int64 _InterlockedCompareExchange64(__int64 volatile * Destination, __int64 Exchange, __int64 Comperand); #if defined(_MSC_VER) #pragma intrinsic(_InterlockedCompareExchange64) #endif #endif // CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS namespace crnlib { #if CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS typedef LONG atomic32_t; typedef LONGLONG atomic64_t; // Returns the original value. inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return InterlockedCompareExchange(pDest, exchange, comparand); } // Returns the original value. inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 7) == 0); return _InterlockedCompareExchange64(pDest, exchange, comparand); } // Returns the resulting incremented value. inline atomic32_t atomic_increment32(atomic32_t volatile *pDest) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return InterlockedIncrement(pDest); } // Returns the resulting decremented value. inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return InterlockedDecrement(pDest); } // Returns the original value. inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return InterlockedExchange(pDest, val); } // Returns the resulting value. inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return InterlockedExchangeAdd(pDest, val) + val; } // Returns the original value. inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return InterlockedExchangeAdd(pDest, val); } #elif CRNLIB_USE_GCC_ATOMIC_BUILTINS typedef long atomic32_t; typedef long long atomic64_t; // Returns the original value. inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return __sync_val_compare_and_swap(pDest, comparand, exchange); } // Returns the original value. inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 7) == 0); return __sync_val_compare_and_swap(pDest, comparand, exchange); } // Returns the resulting incremented value. inline atomic32_t atomic_increment32(atomic32_t volatile *pDest) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return __sync_add_and_fetch(pDest, 1); } // Returns the resulting decremented value. inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return __sync_sub_and_fetch(pDest, 1); } // Returns the original value. inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return __sync_lock_test_and_set(pDest, val); } // Returns the resulting value. inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return __sync_add_and_fetch(pDest, val); } // Returns the original value. inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return __sync_fetch_and_add(pDest, val); } #else #define CRNLIB_NO_ATOMICS 1 // Atomic ops not supported - but try to do something reasonable. Assumes no threading at all. typedef long atomic32_t; typedef long long atomic64_t; inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); atomic32_t cur = *pDest; if (cur == comparand) *pDest = exchange; return cur; } inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 7) == 0); atomic64_t cur = *pDest; if (cur == comparand) *pDest = exchange; return cur; } inline atomic32_t atomic_increment32(atomic32_t volatile *pDest) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return (*pDest += 1); } inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return (*pDest -= 1); } inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); atomic32_t cur = *pDest; *pDest = val; return cur; } inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); return (*pDest += val); } inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val) { CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0); atomic32_t cur = *pDest; *pDest += val; return cur; } #endif } // namespace crnlib #endif // CRN_ATOMICS_H