// // Copyright (c) 2009 Advanced Micro Devices, Inc. All rights reserved. // /*! \file atomic.hpp * \brief Declarations for Memory order access and Atomic operations. * * \author Laurent Morichetti (laurent.morichetti@amd.com) * \date October 2008 */ #ifndef ATOMIC_HPP_ #define ATOMIC_HPP_ #include "top.hpp" #include #ifdef _WIN32 # include #elif defined(ATI_ARCH_X86) # include # include #endif // !_WIN32 #include namespace amd { /*! \addtogroup Threads * @{ * * \defgroup MemOrder Memory ordering * @{ */ /*! \brief Memory order access operations. */ class MemoryOrder : AllStatic { public: /*! \brief Execute a memory fence. * * Perform a serializing operation on loads and stores which guarantees * that all memory operations dispatched prior to the fence will be * globally visible before any other memory operation following the fence. */ static void fence() { # if defined(ATI_ARCH_X86) _mm_mfence(); # else // !ATI_ARCH_X86 __sync_synchronize(); # endif // !ATI_ARCH_X86 } /*! \brief Execute a loads fence. * * Perform a serializing operation on loads which guarantees that all * load from memory operations dispatched prior to the lfence will be * globally visible before any other load following the lfence. */ static void lfence() { # if defined(ATI_ARCH_X86) _mm_lfence(); # else // !ATI_ARCH_X86 fence(); # endif // !ATI_ARCH_X86 } /*! \brief Execute a stores fence. * * Perform a serializing operation on stores which guarantees that all * store to memory operations dispatched prior to the sfence will be * globally visible before any other store following the sfence. */ static void sfence() { # if defined(ATI_ARCH_X86) _mm_sfence(); # else // !ATI_ARCH_X86 fence(); # endif // !ATI_ARCH_X86 } }; /*! @} * \addtogroup Atomic Atomic Operations * @{ */ /*! \brief Static functions for atomic operations. */ class AtomicOperation : AllStatic { private: //! Template to specialize atomic intrinsics on register size. template struct Intrinsics { /*! \brief %Atomic add. * * Atomically add \a inc to \a *dest and return the prior value. */ template static inline T add(T increment, volatile T* dest); /*! \brief %Atomic exchange. * * Atomically exchange value with *dest and return the prior value. */ template static inline T swap(T value, volatile T* dest); /*! \brief %Atomic compare and exchange. * * Atomically compare and xchge value with *dest if *dest == compare. * Return the prior value. */ template static inline T compareAndSwap(T compare, volatile T* dest, T value); /*! \brief %Atomic increment. * * Atomically increment *dest and return the prior value. */ template static inline T increment(volatile T* dest); /*! \brief %Atomic exchange. * * Atomically decrement *dest and return the prior value. */ template static inline T decrement(volatile T* dest); /*! \brief %Atomic or. * * Atomically or \a mask to \a *dest and return the prior value. */ template static inline T _or(T mask, volatile T* dest); /*! \brief %Atomic and. * * Atomically and \a mask to \a *dest and return the prior value. */ template static inline T _and(T mask, volatile T* dest); }; public: /*! \brief %Atomic add. * * Atomically add \a inc to \a *dest and return the prior value. */ template static T add(T inc, volatile T* dest) { return Intrinsics::add((T) inc, dest); } /*! \brief %Atomic exchange. * * Atomically exchange value with *dest and return the prior value. */ template static T swap(T value, volatile T* dest) { return Intrinsics::swap(value, dest); } /*! \brief %Atomic compare and exchange. * * Atomically compare and exchange value with *dest if *dest == compare. * Return the prior value. */ template static T compareAndSwap(T compare, volatile T* dest, T value) { return Intrinsics::compareAndSwap(compare, dest, value); } /*! \brief %Atomic increment. * * Atomically increment *dest and return the prior value. */ template static T increment(volatile T* dest) { return Intrinsics::increment(dest); } /*! \brief %Atomic decrement. * * Atomically decrement *dest and return the prior value. */ template static T decrement(volatile T* dest) { return Intrinsics::decrement(dest); } /*! \brief %Atomic or. * * Atomically or \a mask to \a *dest and return the prior value. */ template static T _or(T mask, volatile T* dest) { return Intrinsics::_or((T) mask, dest); } /*! \brief %Atomic and. * * Atomically or \a mask to \a *dest and return the prior value. */ template static T _and(T mask, volatile T* dest) { return Intrinsics::_and((T) mask, dest); } }; /*@}*/ #if defined(_MSC_VER) template <> template inline T AtomicOperation::Intrinsics<4>::add(T increment, volatile T* dest) { return (T)_InterlockedExchangeAdd( (volatile long*)dest, (long)increment); } template <> template inline T AtomicOperation::Intrinsics<4>::swap(T value, volatile T* dest) { return (T)_InterlockedExchange( (volatile long*)dest, (long)value); } template <> template inline T AtomicOperation::Intrinsics<4>::compareAndSwap( T compare, volatile T* dest, T value) { return (T)_InterlockedCompareExchange( (volatile long*)dest, (long)value, (long)compare); } template <> template inline T AtomicOperation::Intrinsics<4>::increment(volatile T* dest) { return (T)(_InterlockedIncrement((volatile long*)dest) - 1L); } template <> template inline T AtomicOperation::Intrinsics<4>::decrement(volatile T* dest) { return (T)(_InterlockedDecrement((volatile long*)dest) + 1L); } template <> template inline T AtomicOperation::Intrinsics<4>::_or(T mask, volatile T* dest) { return (T)_InterlockedOr( (volatile long*)dest, (long)mask); } template <> template inline T AtomicOperation::Intrinsics<4>::_and(T mask, volatile T* dest) { return (T)_InterlockedAnd( (volatile long*)dest, (long)mask); } #ifdef _WIN64 template <> template inline T AtomicOperation::Intrinsics<8>::add(T increment, volatile T* dest) { return (T)_InterlockedExchangeAdd64( (volatile __int64*)dest, (__int64)increment); } template <> template inline T AtomicOperation::Intrinsics<8>::swap(T value, volatile T* dest) { return (T)_InterlockedExchange64( (volatile __int64*)dest, (__int64)value); } template <> template inline T AtomicOperation::Intrinsics<8>::compareAndSwap( T compare, volatile T* dest, T value) { return (T)_InterlockedCompareExchange64( (volatile __int64*)dest, (__int64)value, (__int64)compare); } template <> template inline T AtomicOperation::Intrinsics<8>::increment(volatile T* dest) { return (T)(_InterlockedIncrement64((volatile __int64*)dest) - 1LL); } template <> template inline T AtomicOperation::Intrinsics<8>::decrement(volatile T* dest) { return (T)(_InterlockedDecrement64((volatile __int64*)dest) + 1LL); } template <> template inline T AtomicOperation::Intrinsics<8>::_or(T mask, volatile T* dest) { return (T)_InterlockedOr64( (volatile long*)dest, (long)mask); } template <> template inline T AtomicOperation::Intrinsics<8>::_and(T mask, volatile T* dest) { return (T)_InterlockedAnd64( (volatile long*)dest, (long)mask); } #endif // _LP64 #elif defined(__GNUC__) template template inline T AtomicOperation::Intrinsics::add(T inc, volatile T* dest) { return __sync_fetch_and_add(dest, inc); } template template inline T AtomicOperation::Intrinsics::swap(T value, volatile T* dest) { return __sync_lock_test_and_set(dest, value); } template template inline T AtomicOperation::Intrinsics::compareAndSwap( T compare, volatile T* dest, T value) { return __sync_val_compare_and_swap(dest, compare, value); } template template inline T AtomicOperation::Intrinsics::increment(volatile T* dest) { return add(T(1), dest); } template template inline T AtomicOperation::Intrinsics::decrement(volatile T* dest) { return add(T(-1), dest); } template template inline T AtomicOperation::Intrinsics::_or(T mask, volatile T* dest) { return __sync_fetch_and_or(dest, mask); } template template inline T AtomicOperation::Intrinsics::_and(T mask, volatile T* dest) { return __sync_fetch_and_and(dest, mask); } #else # error Unimplemented #endif /*! \addtogroup Atomic Atomic Operations * @{ */ /*! \brief A variable of type T with atomic properties. */ template class Atomic { private: typedef typename std::remove_volatile::type>::type>::type value_type; typename std::add_volatile::type value_; //!< \brief The variable. public: //! Construct a new %Atomic variable of type T. Atomic() : value_(T(0)) {} //! Construct a new %Atomic variable of type T from \a value. Atomic(T value) : value_(value) {} //! Construct a new %Atomic variable of type T from another %Atomic. Atomic(const Atomic& atomic) : value_(atomic.value_) { } //! Copy value into this %Atomic variable. Atomic& operator = (T value) { value_ = value; return *this; } //! Return the %Atomic variable value. operator T () const { return T(value_); } //! Return the %Atomic variable value. T operator ->() const { return T(value_); } //! Return the %Atomic variable's address. typename std::add_pointer::type>:: type operator &() { return &value_; } //! Atomically add \a inc to this variable. Atomic& operator += (value_type inc) { if (std::is_pointer::value) { inc *= sizeof(typename std::remove_pointer::type); } AtomicOperation::add(inc, &value_); return *this; } //! Atomically subtract \a inc to this variable. Atomic& operator -= (value_type inc) { value_type modifier = 0; if (std::is_pointer::value) { inc *= sizeof(typename std::remove_pointer::type); } AtomicOperation::add(modifier - inc, &value_); return *this; } //! Atomically OR \a value to this variable. Atomic& operator |= (value_type mask) { AtomicOperation::_or(mask, &value_); return *this; } //! Atomically AND \a value to this variable. Atomic& operator &= (value_type mask) { AtomicOperation::_and(mask, &value_); return *this; } //! Atomically increment this variable and return its new value. typename std::remove_reference::type operator ++ () { if (std::is_pointer::value) { value_type inc = static_cast( sizeof(typename std::remove_pointer::type)); return AtomicOperation::add(inc, &value_) + 1; } else { return AtomicOperation::increment(&value_) + 1; } } //! Atomically decrement this variable and return its new value. typename std::remove_reference::type operator -- () { if (std::is_pointer::value) { value_type inc = static_cast(- static_cast::type>( sizeof(typename std::remove_pointer::type))); return AtomicOperation::add(inc, &value_) - 1; } else { return AtomicOperation::decrement(&value_) - 1; } } //! Atomically increment this variable and return its previous value. typename std::remove_reference::type operator ++ (int) { if (std::is_pointer::value) { value_type inc = static_cast( sizeof(typename std::remove_pointer::type)); return AtomicOperation::add(inc, &value_); } else { return AtomicOperation::increment(&value_); } } //! Atomically decrement this variable and return its previous value. T operator -- (int) { if (std::is_pointer::value) { value_type inc = static_cast(- static_cast::type>( sizeof(typename std::remove_pointer::type))); return AtomicOperation::add(inc, &value_); } else { return AtomicOperation::decrement(&value_); } } /*! \brief Atomically compare this variable with \a compare and set * to value if equals */ bool compareAndSet(T compare, T value) { return compare == AtomicOperation::compareAndSwap( compare, &value_, value); } //! Atomically set this variable to \a value and return its previous value. T swap(T value) { return AtomicOperation::swap(value, &value_); } /*! \brief Execute a stores fence followed by a store to this variable. * * This storeRelease operation ensures that all store to memory operations * preceding this function will be globally visible before the update to * this variable's value. */ void storeRelease(T value) { MemoryOrder::fence(); value_ = value; } /*! \brief Execute a load from this variable followed by a loads fence. * * This loadAcquire operation ensures that all load from memory operations * following this function will be globally visible after the read from * this variable's value. */ T loadAcquire() const { T value = value_; MemoryOrder::fence(); return value; } }; //! Helper function to tie an Atomic to a variable of type T. template inline Atomic make_atomic(T& t) { return Atomic(t); } template class AtomicMarkableReference { private: static const intptr_t kMarkBitMask = 0x1; private: Atomic reference_; private: static intptr_t markMask(bool mark) { return mark ? kMarkBitMask : intptr_t(0); } public: AtomicMarkableReference() : reference_(NULL) { } AtomicMarkableReference(T* ptr, bool mark = false) : reference_((T*)((intptr_t) ptr | markMask(mark))) { } bool compareAndSet( T* expectedPtr, T* newPtr, bool expectedMark, bool newMark) { return reference_.compareAndSet( (T*)((intptr_t) expectedPtr | markMask(expectedMark)), (T*)((intptr_t) newPtr | markMask(newMark))); } std::pair swap(T* newPtr, bool newMark) { T* prev = reference_.swap( (T*)((intptr_t) newPtr | markMask(newMark))); return std::make_pair( (T*) ((intptr_t) prev & ~kMarkBitMask), ((intptr_t) prev & kMarkBitMask) != 0); } bool tryMark(T* expectedPtr, bool newMark) { T* current = reference_; if (((intptr_t) current & ~kMarkBitMask) != (intptr_t) expectedPtr) { return false; } bool currentMark = ((intptr_t) current & kMarkBitMask) != 0; return currentMark == newMark || reference_.compareAndSet(current, (T*)((intptr_t) expectedPtr | markMask(newMark))); } bool isMarked() const { return ((intptr_t)(T*) reference_ & kMarkBitMask) != 0; } std::pair get() const { T* current = reference_; return std::make_pair( (T*) ((intptr_t) current & ~kMarkBitMask), ((intptr_t) current & kMarkBitMask) != 0); } T* getReference() const { return (T*) ((intptr_t)(T*) reference_ & ~kMarkBitMask); } void set(T* ptr, bool mark) { reference_ = (T*)((intptr_t) ptr | markMask(mark)); } }; /*! @} * @} */ } // namespace amd #endif /*ATOMIC_HPP_*/