#pragma once #if defined(__GNUG__) template inline std::enable_if_t sync_val_compare_and_swap(volatile T* dest, T2 comp, T2 exch) { return __sync_val_compare_and_swap(dest, comp, exch); } template inline std::enable_if_t sync_bool_compare_and_swap(volatile T* dest, T2 comp, T2 exch) { return __sync_bool_compare_and_swap(dest, comp, exch); } template inline std::enable_if_t sync_lock_test_and_set(volatile T* dest, T2 value) { return __sync_lock_test_and_set(dest, value); } template inline std::enable_if_t sync_fetch_and_add(volatile T* dest, T2 value) { return __sync_fetch_and_add(dest, value); } template inline std::enable_if_t sync_fetch_and_sub(volatile T* dest, T2 value) { return __sync_fetch_and_sub(dest, value); } template inline std::enable_if_t sync_fetch_and_or(volatile T* dest, T2 value) { return __sync_fetch_and_or(dest, value); } template inline std::enable_if_t sync_fetch_and_and(volatile T* dest, T2 value) { return __sync_fetch_and_and(dest, value); } template inline std::enable_if_t sync_fetch_and_xor(volatile T* dest, T2 value) { return __sync_fetch_and_xor(dest, value); } #elif defined(_MSC_VER) // atomic compare and swap functions inline u8 sync_val_compare_and_swap(volatile u8* dest, u8 comp, u8 exch) { return _InterlockedCompareExchange8((volatile char*)dest, exch, comp); } inline u16 sync_val_compare_and_swap(volatile u16* dest, u16 comp, u16 exch) { return _InterlockedCompareExchange16((volatile short*)dest, exch, comp); } inline u32 sync_val_compare_and_swap(volatile u32* dest, u32 comp, u32 exch) { return _InterlockedCompareExchange((volatile long*)dest, exch, comp); } inline u64 sync_val_compare_and_swap(volatile u64* dest, u64 comp, u64 exch) { return _InterlockedCompareExchange64((volatile long long*)dest, exch, comp); } inline u128 sync_val_compare_and_swap(volatile u128* dest, u128 comp, u128 exch) { _InterlockedCompareExchange128((volatile long long*)dest, exch.hi, exch.lo, (long long*)&comp); return comp; } inline bool sync_bool_compare_and_swap(volatile u8* dest, u8 comp, u8 exch) { return (u8)_InterlockedCompareExchange8((volatile char*)dest, exch, comp) == comp; } inline bool sync_bool_compare_and_swap(volatile u16* dest, u16 comp, u16 exch) { return (u16)_InterlockedCompareExchange16((volatile short*)dest, exch, comp) == comp; } inline bool sync_bool_compare_and_swap(volatile u32* dest, u32 comp, u32 exch) { return (u32)_InterlockedCompareExchange((volatile long*)dest, exch, comp) == comp; } inline bool sync_bool_compare_and_swap(volatile u64* dest, u64 comp, u64 exch) { return (u64)_InterlockedCompareExchange64((volatile long long*)dest, exch, comp) == comp; } inline bool sync_bool_compare_and_swap(volatile u128* dest, u128 comp, u128 exch) { return _InterlockedCompareExchange128((volatile long long*)dest, exch.hi, exch.lo, (long long*)&comp) != 0; } // atomic exchange functions inline u8 sync_lock_test_and_set(volatile u8* dest, u8 value) { return _InterlockedExchange8((volatile char*)dest, value); } inline u16 sync_lock_test_and_set(volatile u16* dest, u16 value) { return _InterlockedExchange16((volatile short*)dest, value); } inline u32 sync_lock_test_and_set(volatile u32* dest, u32 value) { return _InterlockedExchange((volatile long*)dest, value); } inline u64 sync_lock_test_and_set(volatile u64* dest, u64 value) { return _InterlockedExchange64((volatile long long*)dest, value); } inline u128 sync_lock_test_and_set(volatile u128* dest, u128 value) { while (true) { u128 old; old.lo = dest->lo; old.hi = dest->hi; if (sync_bool_compare_and_swap(dest, old, value)) return old; } } // atomic add functions inline u8 sync_fetch_and_add(volatile u8* dest, u8 value) { return _InterlockedExchangeAdd8((volatile char*)dest, value); } inline u16 sync_fetch_and_add(volatile u16* dest, u16 value) { return _InterlockedExchangeAdd16((volatile short*)dest, value); } inline u32 sync_fetch_and_add(volatile u32* dest, u32 value) { return _InterlockedExchangeAdd((volatile long*)dest, value); } inline u64 sync_fetch_and_add(volatile u64* dest, u64 value) { return _InterlockedExchangeAdd64((volatile long long*)dest, value); } inline u128 sync_fetch_and_add(volatile u128* dest, u128 value) { while (true) { u128 old; old.lo = dest->lo; old.hi = dest->hi; if (sync_bool_compare_and_swap(dest, old, old + value)) return old; } } // atomic sub functions inline u8 sync_fetch_and_sub(volatile u8* dest, u8 value) { return _InterlockedExchangeAdd8((volatile char*)dest, -(char)value); } inline u16 sync_fetch_and_sub(volatile u16* dest, u16 value) { return _InterlockedExchangeAdd16((volatile short*)dest, -(short)value); } inline u32 sync_fetch_and_sub(volatile u32* dest, u32 value) { return _InterlockedExchangeAdd((volatile long*)dest, -(long)value); } inline u64 sync_fetch_and_sub(volatile u64* dest, u64 value) { return _InterlockedExchangeAdd64((volatile long long*)dest, -(long long)value); } inline u128 sync_fetch_and_sub(volatile u128* dest, u128 value) { while (true) { u128 old; old.lo = dest->lo; old.hi = dest->hi; if (sync_bool_compare_and_swap(dest, old, old - value)) return old; } } // atomic `bitwise or` functions inline u8 sync_fetch_and_or(volatile u8* dest, u8 value) { return _InterlockedOr8((volatile char*)dest, value); } inline u16 sync_fetch_and_or(volatile u16* dest, u16 value) { return _InterlockedOr16((volatile short*)dest, value); } inline u32 sync_fetch_and_or(volatile u32* dest, u32 value) { return _InterlockedOr((volatile long*)dest, value); } inline u64 sync_fetch_and_or(volatile u64* dest, u64 value) { return _InterlockedOr64((volatile long long*)dest, value); } inline u128 sync_fetch_and_or(volatile u128* dest, u128 value) { while (true) { u128 old; old.lo = dest->lo; old.hi = dest->hi; if (sync_bool_compare_and_swap(dest, old, old | value)) return old; } } // atomic `bitwise and` functions inline u8 sync_fetch_and_and(volatile u8* dest, u8 value) { return _InterlockedAnd8((volatile char*)dest, value); } inline u16 sync_fetch_and_and(volatile u16* dest, u16 value) { return _InterlockedAnd16((volatile short*)dest, value); } inline u32 sync_fetch_and_and(volatile u32* dest, u32 value) { return _InterlockedAnd((volatile long*)dest, value); } inline u64 sync_fetch_and_and(volatile u64* dest, u64 value) { return _InterlockedAnd64((volatile long long*)dest, value); } inline u128 sync_fetch_and_and(volatile u128* dest, u128 value) { while (true) { u128 old; old.lo = dest->lo; old.hi = dest->hi; if (sync_bool_compare_and_swap(dest, old, old & value)) return old; } } // atomic `bitwise xor` functions inline u8 sync_fetch_and_xor(volatile u8* dest, u8 value) { return _InterlockedXor8((volatile char*)dest, value); } inline u16 sync_fetch_and_xor(volatile u16* dest, u16 value) { return _InterlockedXor16((volatile short*)dest, value); } inline u32 sync_fetch_and_xor(volatile u32* dest, u32 value) { return _InterlockedXor((volatile long*)dest, value); } inline u64 sync_fetch_and_xor(volatile u64* dest, u64 value) { return _InterlockedXor64((volatile long long*)dest, value); } inline u128 sync_fetch_and_xor(volatile u128* dest, u128 value) { while (true) { u128 old; old.lo = dest->lo; old.hi = dest->hi; if (sync_bool_compare_and_swap(dest, old, old ^ value)) return old; } } #endif /* _MSC_VER */ template struct atomic_storage { static_assert(!Size, "Invalid atomic type"); }; template struct atomic_storage { using type = u8; }; template struct atomic_storage { using type = u16; }; template struct atomic_storage { using type = u32; }; template struct atomic_storage { using type = u64; }; template struct atomic_storage { using type = u128; }; template using atomic_storage_t = typename atomic_storage::type; // atomic result wrapper; implements special behaviour for void result type template struct atomic_op_result_t { RT result; template atomic_op_result_t(T func, VT& var, Args&&... args) : result(std::move(func(var, std::forward(args)...))) { } RT move() { return std::move(result); } }; // void specialization: result is the initial value of the first arg template struct atomic_op_result_t { VT result; template atomic_op_result_t(T func, VT& var, Args&&... args) : result(var) { func(var, std::forward(args)...); } VT move() { return std::move(result); } }; // member function specialization template struct atomic_op_result_t { RT result; template atomic_op_result_t(RT(CT::*func)(FArgs...), VT& var, Args&&... args) : result(std::move((var.*func)(std::forward(args)...))) { } RT move() { return std::move(result); } }; // member function void specialization template struct atomic_op_result_t { VT result; template atomic_op_result_t(void(CT::*func)(FArgs...), VT& var, Args&&... args) : result(var) { (var.*func)(std::forward(args)...); } VT move() { return std::move(result); } }; // Atomic type with lock-free and standard layout guarantees (and appropriate limitations) template class atomic_t { using type = std::remove_cv_t; using stype = atomic_storage_t; using storage = atomic_storage; static_assert(alignof(type) <= alignof(stype), "atomic_t<> error: unexpected alignment"); stype m_data; template static inline void write_relaxed(volatile T2& data, const T2& value) { data = value; } static inline void write_relaxed(volatile u128& data, const u128& value) { sync_lock_test_and_set(&data, value); } template static inline T2 read_relaxed(const volatile T2& data) { return data; } static inline u128 read_relaxed(const volatile u128& value) { return sync_val_compare_and_swap(const_cast(&value), u128{0}, u128{0}); } public: static inline const stype to_subtype(const type& value) { return reinterpret_cast(value); } static inline const type from_subtype(const stype value) { return reinterpret_cast(value); } atomic_t() = default; atomic_t(const atomic_t&) = delete; atomic_t(type value) : m_data(to_subtype(value)) { } atomic_t& operator =(const atomic_t&) = delete; atomic_t& operator =(type value) { return write_relaxed(m_data, to_subtype(value)), *this; } operator type() const volatile { return from_subtype(read_relaxed(m_data)); } // Unsafe direct access stype* raw_data() { return reinterpret_cast(&m_data); } // Unsafe direct access type& raw() { return reinterpret_cast(m_data); } // Atomically compare data with cmp, replace with exch if equal, return previous data value anyway type compare_and_swap(const type& cmp, const type& exch) volatile { return from_subtype(sync_val_compare_and_swap(&m_data, to_subtype(cmp), to_subtype(exch))); } // Atomically compare data with cmp, replace with exch if equal, return true if data was replaced bool compare_and_swap_test(const type& cmp, const type& exch) volatile { return sync_bool_compare_and_swap(&m_data, to_subtype(cmp), to_subtype(exch)); } // Atomically replace data with exch, return previous data value type exchange(const type& exch) volatile { return from_subtype(sync_lock_test_and_set(&m_data, to_subtype(exch))); } // Atomically read data, possibly without memory barrier (not for 128 bit) type load() const volatile { return from_subtype(read_relaxed(m_data)); } // Atomically write data, possibly without memory barrier (not for 128 bit) void store(const type& value) volatile { write_relaxed(m_data, to_subtype(value)); } // Perform an atomic operation on data (func is either pointer to member function or callable object with a T& first arg); // Returns the result of the callable object call or previous (old) value of the atomic variable if the return type is void template> auto atomic_op(F func, Args&&... args) volatile -> decltype(atomic_op_result_t::result) { while (true) { // Read the old value from memory const stype old = read_relaxed(m_data); // Copy the old value stype _new = old; // Call atomic op for the local copy of the old value and save the return value of the function atomic_op_result_t result(func, reinterpret_cast(_new), args...); // Atomically compare value with `old`, replace with `_new` and return on success if (sync_bool_compare_and_swap(&m_data, old, _new)) return result.move(); } } // Atomic bitwise OR, returns previous data type _or(const type& right) volatile { return from_subtype(sync_fetch_and_or(&m_data, to_subtype(right))); } // Atomic bitwise AND, returns previous data type _and(const type& right) volatile { return from_subtype(sync_fetch_and_and(&m_data, to_subtype(right))); } // Atomic bitwise AND NOT (inverts right argument), returns previous data type _and_not(const type& right) volatile { return from_subtype(sync_fetch_and_and(&m_data, ~to_subtype(right))); } // Atomic bitwise XOR, returns previous data type _xor(const type& right) volatile { return from_subtype(sync_fetch_and_xor(&m_data, to_subtype(right))); } type operator |=(const type& right) volatile { return from_subtype(sync_fetch_and_or(&m_data, to_subtype(right)) | to_subtype(right)); } type operator &=(const type& right) volatile { return from_subtype(sync_fetch_and_and(&m_data, to_subtype(right)) & to_subtype(right)); } type operator ^=(const type& right) volatile { return from_subtype(sync_fetch_and_xor(&m_data, to_subtype(right)) ^ to_subtype(right)); } }; template inline std::enable_if_t operator ++(atomic_t& left) { return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1) + 1); } template inline std::enable_if_t operator --(atomic_t& left) { return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1) - 1); } template inline std::enable_if_t operator ++(atomic_t& left, int) { return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1)); } template inline std::enable_if_t operator --(atomic_t& left, int) { return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1)); } template inline std::enable_if_t::value, T> operator +=(atomic_t& left, const T2& right) { return left.from_subtype(sync_fetch_and_add(left.raw_data(), right) + right); } template inline std::enable_if_t::value, T> operator -=(atomic_t& left, const T2& right) { return left.from_subtype(sync_fetch_and_sub(left.raw_data(), right) - right); } template inline std::enable_if_t> operator ++(atomic_t>& left) { return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1) + 1); } template inline std::enable_if_t> operator --(atomic_t>& left) { return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1) - 1); } template inline std::enable_if_t> operator ++(atomic_t>& left, int) { return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1)); } template inline std::enable_if_t> operator --(atomic_t>& left, int) { return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1)); } template inline std::enable_if_t::value, nse_t> operator +=(atomic_t>& left, const T2& right) { return left.from_subtype(sync_fetch_and_add(left.raw_data(), right) + right); } template inline std::enable_if_t::value, nse_t> operator -=(atomic_t>& left, const T2& right) { return left.from_subtype(sync_fetch_and_sub(left.raw_data(), right) - right); } template inline std::enable_if_t> operator ++(atomic_t>& left) { return left.atomic_op([](se_t& value) -> se_t { return ++value; }); } template inline std::enable_if_t> operator --(atomic_t>& left) { return left.atomic_op([](se_t& value) -> se_t { return --value; }); } template inline std::enable_if_t> operator ++(atomic_t>& left, int) { return left.atomic_op([](se_t& value) -> se_t { return value++; }); } template inline std::enable_if_t> operator --(atomic_t>& left, int) { return left.atomic_op([](se_t& value) -> se_t { return value--; }); } template inline std::enable_if_t::value, se_t> operator +=(atomic_t>& left, const T2& right) { return left.atomic_op([&](se_t& value) -> se_t { return value += right; }); } template inline std::enable_if_t::value, se_t> operator -=(atomic_t>& left, const T2& right) { return left.atomic_op([&](se_t& value) -> se_t { return value -= right; }); } // Atomic BE Type (for PS3 virtual memory) template using atomic_be_t = atomic_t>; // Atomic LE Type (for PSV virtual memory) template using atomic_le_t = atomic_t>; // Algorithm for std::atomic; similar to atomic_t::atomic_op() template> auto atomic_op(std::atomic& var, F func, Args&&... args) -> decltype(atomic_op_result_t::result) { auto old = var.load(); while (true) { auto _new = old; atomic_op_result_t result(func, _new, args...); if (var.compare_exchange_strong(old, _new)) return result.move(); } }