diff --git a/src/nvthread/Atomic.h b/src/nvthread/Atomic.h new file mode 100644 index 0000000..fbc2694 --- /dev/null +++ b/src/nvthread/Atomic.h @@ -0,0 +1,144 @@ +// This code is in the public domain -- castanyo@yahoo.es + +#ifndef NV_THREAD_ATOMIC_H +#define NV_THREAD_ATOMIC_H + +#include "nvthread.h" + +#include "nvcore/Debug.h" + + +#if NV_CC_MSVC + +#include // Already included by nvthread.h + +#pragma intrinsic(_InterlockedIncrement, _InterlockedDecrement) +#pragma intrinsic(_InterlockedCompareExchange, _InterlockedExchange) + +/* +extern "C" +{ + #pragma intrinsic(_InterlockedIncrement, _InterlockedDecrement) + LONG __cdecl _InterlockedIncrement(long volatile *Addend); + LONG __cdecl _InterlockedDecrement(long volatile *Addend); + + #pragma intrinsic(_InterlockedCompareExchange, _InterlockedExchange) + LONG __cdecl _InterlockedCompareExchange(long volatile * Destination, long Exchange, long Compared); + LONG __cdecl _InterlockedExchange(long volatile * Target, LONG Value); +} +*/ + +#endif // NV_CC_MSVC + + +namespace nv { + + // Load and stores. + inline uint32 loadRelaxed(const uint32 * ptr) { return *ptr; } + inline void storeRelaxed(uint32 * ptr, uint32 value) { *ptr = value; } + + inline uint32 loadAcquire(const volatile uint32 * ptr) + { + nvDebugCheck((intptr_t(ptr) & 3) == 0); + +#if POSH_CPU_X86 || POSH_CPU_X86_64 + nvCompilerReadBarrier(); + uint32 ret = *ptr; // on x86, loads are Acquire + nvCompilerReadBarrier(); + return ret; +#else +#error "Not implemented" +#endif + } + + inline void storeRelease(volatile uint32 * ptr, uint32 value) + { + nvDebugCheck((intptr_t(ptr) & 3) == 0); + nvDebugCheck((intptr_t(&value) & 3) == 0); + +#if POSH_CPU_X86 || POSH_CPU_X86_64 + nvCompilerWriteBarrier(); + *ptr = value; // on x86, stores are Release + nvCompilerWriteBarrier(); +#else +#error "Not implemented" +#endif + } + + + // Atomics. @@ Assuming sequential memory order? + +#if NV_CC_MSVC + NV_COMPILER_CHECK(sizeof(uint32) == sizeof(long)); + + inline uint32 atomicIncrement(uint32 * value) + { + nvDebugCheck((intptr_t(value) & 3) == 0); + + return (uint32)_InterlockedIncrement((long *)value); + } + + inline uint32 atomicDecrement(uint32 * value) + { + nvDebugCheck((intptr_t(value) & 3) == 0); + + return (uint32)_InterlockedDecrement((long *)value); + } +#endif + + + + + // It would be nice to have C++0x-style atomic types, but I'm not in the mood right now. Only uint32 supported so far. +#if 0 + template + void increment(T * value); + + template + void decrement(T * value); + + template <> + void increment(uint32 * value) { + } + + template <> + void increment(uint64 * value) { + } + + + + template + class Atomic + { + public: + explicit Atomic() : m_value() { } + explicit Atomic( T val ) : m_value(val) { } + ~Atomic() { } + + T loadRelaxed() const { return m_value; } + void storeRelaxed(T val) { m_value = val; } + + //T loadAcquire() const volatile { return nv::loadAcquire(&m_value); } + //void storeRelease(T val) volatile { nv::storeRelease(&m_value, val); } + + void increment() /*volatile*/ { nv::atomicIncrement(m_value); } + void decrement() /*volatile*/ { nv::atomicDecrement(m_value); } + + void compareAndStore(T oldVal, T newVal) { nv::atomicCompareAndStore(&m_value, oldVal, newVal); } + T compareAndExchange(T oldVal, T newVal) { nv::atomicCompareAndStore(&m_value, oldVal, newVal); } + T exchange(T newVal) { nv::atomicExchange(&m_value, newVal); } + + private: + // don't provide operator = or == ; make the client write Store( Load() ) + NV_FORBID_COPY(Atomic); + + NV_COMPILER_CHECK(sizeof(T) == sizeof(uint32) || sizeof(T) == sizeof(uint64)); + + T m_value; + }; +#endif + +} // nv namespace + + +#endif // NV_THREADS_ATOMICS_H