From 8e7cc759f5dcd5d3ceaab415d47fc9d98df765bb Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 9 Jun 2017 20:38:10 +0100 Subject: [PATCH] Made JUCE's Atomic class a simple wrapper around std::atomic on platforms that support it --- .../native/juce_android_OpenSL.cpp | 2 +- modules/juce_core/memory/juce_Atomic.h | 828 +++++++++--------- .../juce_core/system/juce_CompilerSupport.h | 11 + .../juce_core/system/juce_StandardHeader.h | 5 + modules/juce_core/threads/juce_Thread.cpp | 6 +- 5 files changed, 424 insertions(+), 428 deletions(-) diff --git a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index eb7def6c96..f39e7d05e3 100644 --- a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -542,7 +542,7 @@ public: if (old == callbackToUse) break; - if (callback.compareAndSetValue (callbackToUse, old) == old) + if (callback.compareAndSetBool (callbackToUse, old)) break; Thread::sleep (1); diff --git a/modules/juce_core/memory/juce_Atomic.h b/modules/juce_core/memory/juce_Atomic.h index eb6354d573..65408b4c01 100644 --- a/modules/juce_core/memory/juce_Atomic.h +++ b/modules/juce_core/memory/juce_Atomic.h @@ -22,469 +22,453 @@ #pragma once -//============================================================================== #ifndef DOXYGEN - template class AtomicBase; + namespace AtomicHelpers + { + template struct DiffTypeHelper { typedef T Type; }; + template struct DiffTypeHelper { typedef std::ptrdiff_t Type; }; + } #endif -//============================================================================== -/** - Simple class to hold a primitive value and perform atomic operations on it. +#if JUCE_ATOMIC_AVAILABLE + //============================================================================== + /** + A simple wrapper around std::atomic. + */ + template + struct Atomic + { + typedef typename AtomicHelpers::DiffTypeHelper::Type DiffType; - The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. - There are methods to perform most of the basic atomic operations. -*/ -template -class Atomic : public AtomicBase -{ -public: - /** Resulting type when subtracting the underlying Type. */ - typedef typename AtomicBase::DiffType DiffType; + /** Creates a new value, initialised to zero. */ + Atomic() noexcept : value (0) {} - /** Creates a new value, initialised to zero. */ - inline Atomic() noexcept {} + /** Creates a new value, with a given initial value. */ + Atomic (const Type initialValue) noexcept : value (initialValue) {} - /** Creates a new value, with a given initial value. */ - inline explicit Atomic (const Type initialValue) noexcept : AtomicBase (initialValue) {} + /** Copies another value (atomically). */ + Atomic (const Atomic& other) noexcept : value (other.get()) {} - /** Copies another value (atomically). */ - inline Atomic (const Atomic& other) noexcept : AtomicBase (other) {} + /** Destructor. */ + ~Atomic() noexcept + { + #if __cpp_lib_atomic_is_always_lock_free + static_assert (std::atomic::is_always_lock_free(), + "This class can only be used for lock-free types"); + #endif + } - /** Destructor. */ - inline ~Atomic() noexcept - { - static_assert (sizeof (Type) == 4 || sizeof (Type) == 8, - "Atomic can only be used for types which are 32 or 64 bits in size"); - } + /** Atomically reads and returns the current value. */ + Type get() const noexcept { return value.load(); } - /** Atomically reads and returns the current value. */ - inline Type get() const noexcept { return AtomicBase::get(); } + /** Atomically sets the current value. */ + void set (Type newValue) noexcept { value = newValue; } - /** Copies another value onto this one (atomically). */ - inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase::operator= (other); return *this; } + /** Atomically sets the current value, returning the value that was replaced. */ + Type exchange (Type newValue) noexcept { return value.exchange (newValue); } - /** Copies another value onto this one (atomically). */ - inline Atomic& operator= (const Type newValue) noexcept { AtomicBase::operator= (newValue); return *this; } + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. - /** Atomically sets the current value. */ - inline void set (Type newValue) noexcept { exchange (newValue); } + This operation is the atomic equivalent of doing this: + @code + bool compareAndSetBool (Type newValue, Type valueToCompare) + { + if (get() == valueToCompare) + { + set (newValue); + return true; + } - /** Atomically sets the current value, returning the value that was replaced. */ - inline Type exchange (Type v) noexcept { return AtomicBase::exchange (v); } + return false; + } + @endcode - /** Atomically adds a number to this value, returning the new value. */ - Type operator+= (DiffType amountToAdd) noexcept; + Internally, this method calls std::atomic::compare_exchange_strong with + memory_order_seq_cst (the strictest std::memory_order). - /** Atomically subtracts a number from this value, returning the new value. */ - Type operator-= (DiffType amountToSubtract) noexcept; + @returns true if the comparison was true and the value was replaced; false if + the comparison failed and the value was left unchanged. + @see compareAndSetValue + */ + bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept + { + return value.compare_exchange_strong (valueToCompare, newValue); + } - /** Atomically increments this value, returning the new value. */ - Type operator++() noexcept; + /** Copies another value into this one (atomically). */ + Atomic& operator= (const Atomic& other) noexcept + { + value = other.value.load(); + return *this; + } - /** Atomically decrements this value, returning the new value. */ - Type operator--() noexcept; + /** Copies another value into this one (atomically). */ + Atomic& operator= (const Type newValue) noexcept + { + value = newValue; + return *this; + } - /** Atomically compares this value with a target value, and if it is equal, sets - this to be equal to a new value. + /** Atomically adds a number to this value, returning the new value. */ + Type operator+= (DiffType amountToAdd) noexcept { return value += amountToAdd; } - This operation is the atomic equivalent of doing this: - @code - bool compareAndSetBool (Type newValue, Type valueToCompare) - { - if (get() == valueToCompare) - { - set (newValue); - return true; - } + /** Atomically subtracts a number from this value, returning the new value. */ + Type operator-= (DiffType amountToSubtract) noexcept { return value -= amountToSubtract; } - return false; - } - @endcode + /** Atomically increments this value, returning the new value. */ + Type operator++() noexcept { return ++value; } - @returns true if the comparison was true and the value was replaced; false if - the comparison failed and the value was left unchanged. - @see compareAndSetValue - */ - inline bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept { return AtomicBase::compareAndSetBool (newValue, valueToCompare); } + /** Atomically decrements this value, returning the new value. */ + Type operator--() noexcept { return --value; } - /** Atomically compares this value with a target value, and if it is equal, sets - this to be equal to a new value. + /** Implements a memory read/write barrier. - This operation is the atomic equivalent of doing this: - @code - Type compareAndSetValue (Type newValue, Type valueToCompare) - { - Type oldValue = get(); - if (oldValue == valueToCompare) - set (newValue); + Internally this calls std::atomic_thread_fence with + memory_order_seq_cst (the strictest std::memory_order). + */ + void memoryBarrier() noexcept { atomic_thread_fence (std::memory_order_seq_cst); } - return oldValue; - } - @endcode + /** The std::atomic object that this class operates on. */ + std::atomic value; - @returns the old value before it was changed. - @see compareAndSetBool - */ - inline Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept { return AtomicBase::compareAndSetValue (newValue, valueToCompare); } - - /** Implements a memory read/write barrier. */ - static inline void memoryBarrier() noexcept { AtomicBase::memoryBarrier (); } -}; - -#ifndef DOXYGEN - -//============================================================================== -// Internal implementation follows -//============================================================================== -template struct DiffTypeHelper { typedef T Type; }; -template struct DiffTypeHelper { typedef std::ptrdiff_t Type; }; - -template -class AtomicBase -{ -public: - typedef typename DiffTypeHelper::Type DiffType; - - inline AtomicBase() noexcept : value (0) {} - inline explicit AtomicBase (const Type v) noexcept : value (v) {} - inline AtomicBase (const AtomicBase& other) noexcept : value (other.get()) {} - Type get() const noexcept; - inline AtomicBase& operator= (const AtomicBase& other) noexcept { exchange (other.get()); return *this; } - inline AtomicBase& operator= (const Type newValue) noexcept { exchange (newValue); return *this; } - void set (Type newValue) noexcept { exchange (newValue); } - Type exchange (Type) noexcept; - bool compareAndSetBool (Type, Type) noexcept; - Type compareAndSetValue (Type, Type) noexcept; - static void memoryBarrier() noexcept; - - //============================================================================== - #if JUCE_64BIT - JUCE_ALIGN (8) - #else - JUCE_ALIGN (4) - #endif - - /** The raw value that this class operates on. - This is exposed publicly in case you need to manipulate it directly - for performance reasons. - */ - volatile Type value; - -protected: - template - static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; } - - static inline Type castFrom32Bit (int32 value) noexcept { return castTo (value); } - static inline Type castFrom64Bit (int64 value) noexcept { return castTo (value); } - static inline int32 castTo32Bit (Type value) noexcept { return castTo (value); } - static inline int64 castTo64Bit (Type value) noexcept { return castTo (value); } - - Type operator++ (int); // better to just use pre-increment with atomics.. - Type operator-- (int); - - /** This templated negate function will negate pointers as well as integers */ - template - inline ValueType negateValue (ValueType n) noexcept - { - return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n - : (sizeof (ValueType) == 2 ? (ValueType) -(short) n - : (sizeof (ValueType) == 4 ? (ValueType) -(int) n - : ((ValueType) -(int64) n))); - } - - /** This templated negate function will negate pointers as well as integers */ - template - inline PointerType* negateValue (PointerType* n) noexcept - { - return reinterpret_cast (-reinterpret_cast (n)); - } -}; - -//============================================================================== -// Specialisation for void* which does not include the pointer arithmetic -template <> -class Atomic : public AtomicBase -{ -public: - inline Atomic() noexcept {} - inline explicit Atomic (void* const initialValue) noexcept : AtomicBase (initialValue) {} - inline Atomic (const Atomic& other) noexcept : AtomicBase (other) {} - inline void* get() const noexcept { return AtomicBase::get(); } - inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase::operator= (other); return *this; } - inline Atomic& operator= (void* const newValue) noexcept { AtomicBase::operator= (newValue); return *this; } - inline void set (void* newValue) noexcept { exchange (newValue); } - inline void* exchange (void* v) noexcept { return AtomicBase::exchange (v); } - inline bool compareAndSetBool (void* newValue, void* valueToCompare) noexcept { return AtomicBase::compareAndSetBool (newValue, valueToCompare); } - inline void* compareAndSetValue (void* newValue, void* valueToCompare) noexcept { return AtomicBase::compareAndSetValue (newValue, valueToCompare); } - static inline void memoryBarrier() noexcept { AtomicBase::memoryBarrier(); } -}; - -//============================================================================== -/* - The following code is in the header so that the atomics can be inlined where possible... -*/ -#if JUCE_MAC && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)) - #define JUCE_ATOMICS_MAC_LEGACY 1 // Older OSX builds using gcc4.1 or earlier - -//============================================================================== -#elif JUCE_GCC || JUCE_CLANG - #define JUCE_ATOMICS_GCC 1 // GCC with intrinsics - - #if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) - #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 - #endif - -//============================================================================== -#else - #define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics - - #ifndef __INTEL_COMPILER - #pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \ - _InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier) - #endif - #define juce_InterlockedExchange(a, b) _InterlockedExchange(a, b) - #define juce_InterlockedIncrement(a) _InterlockedIncrement(a) - #define juce_InterlockedDecrement(a) _InterlockedDecrement(a) - #define juce_InterlockedExchangeAdd(a, b) _InterlockedExchangeAdd(a, b) - #define juce_InterlockedCompareExchange(a, b, c) _InterlockedCompareExchange(a, b, c) - #define juce_InterlockedCompareExchange64(a, b, c) _InterlockedCompareExchange64(a, b, c) - #define juce_MemoryBarrier _ReadWriteBarrier - - #if JUCE_64BIT - #ifndef __INTEL_COMPILER - #pragma intrinsic (_InterlockedExchangeAdd64, _InterlockedExchange64, _InterlockedIncrement64, _InterlockedDecrement64) + //============================================================================== + #ifndef DOXYGEN + // This method has been deprecated as there is no equivalent method in std::atomic. + JUCE_DEPRECATED (Type compareAndSetValue (Type, Type) noexcept); #endif - #define juce_InterlockedExchangeAdd64(a, b) _InterlockedExchangeAdd64(a, b) - #define juce_InterlockedExchange64(a, b) _InterlockedExchange64(a, b) - #define juce_InterlockedIncrement64(a) _InterlockedIncrement64(a) - #define juce_InterlockedDecrement64(a) _InterlockedDecrement64(a) - #else - // None of these atomics are available in a 32-bit Windows build!! - template static Type juce_InterlockedExchangeAdd64 (volatile Type* a, Type b) noexcept { jassertfalse; Type old = *a; *a += b; return old; } - template static Type juce_InterlockedExchange64 (volatile Type* a, Type b) noexcept { jassertfalse; Type old = *a; *a = b; return old; } - template static Type juce_InterlockedIncrement64 (volatile Type* a) noexcept { jassertfalse; return ++*a; } - template static Type juce_InterlockedDecrement64 (volatile Type* a) noexcept { jassertfalse; return --*a; } + }; + +#else + + #ifndef DOXYGEN + template class AtomicBase; + #endif + + //============================================================================== + /** + Simple class to hold a primitive value and perform atomic operations on it. + + The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. + There are methods to perform most of the basic atomic operations. + */ + template + class Atomic : public AtomicBase + { + public: + /** Resulting type when subtracting the underlying Type. */ + typedef typename AtomicBase::DiffType DiffType; + + /** Creates a new value, initialised to zero. */ + inline Atomic() noexcept {} + + /** Creates a new value, with a given initial value. */ + inline explicit Atomic (const Type initialValue) noexcept : AtomicBase (initialValue) {} + + /** Copies another value (atomically). */ + inline Atomic (const Atomic& other) noexcept : AtomicBase (other) {} + + /** Destructor. */ + inline ~Atomic() noexcept + { + static_assert (sizeof (Type) == 4 || sizeof (Type) == 8, + "Atomic can only be used for types which are 32 or 64 bits in size"); + } + + /** Atomically reads and returns the current value. */ + inline Type get() const noexcept { return AtomicBase::get(); } + + /** Copies another value into this one (atomically). */ + inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase::operator= (other); return *this; } + + /** Copies another value into this one (atomically). */ + inline Atomic& operator= (const Type newValue) noexcept { AtomicBase::operator= (newValue); return *this; } + + /** Atomically sets the current value. */ + inline void set (Type newValue) noexcept { exchange (newValue); } + + /** Atomically sets the current value, returning the value that was replaced. */ + inline Type exchange (Type v) noexcept { return AtomicBase::exchange (v); } + + /** Atomically adds a number to this value, returning the new value. */ + Type operator+= (DiffType amountToAdd) noexcept; + + /** Atomically subtracts a number from this value, returning the new value. */ + Type operator-= (DiffType amountToSubtract) noexcept; + + /** Atomically increments this value, returning the new value. */ + Type operator++() noexcept; + + /** Atomically decrements this value, returning the new value. */ + Type operator--() noexcept; + + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. + + This operation is the atomic equivalent of doing this: + @code + bool compareAndSetBool (Type newValue, Type valueToCompare) + { + if (get() == valueToCompare) + { + set (newValue); + return true; + } + + return false; + } + @endcode + + @returns true if the comparison was true and the value was replaced; false if + the comparison failed and the value was left unchanged. + @see compareAndSetValue + */ + inline bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept { return AtomicBase::compareAndSetBool (newValue, valueToCompare); } + + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. + + This operation is the atomic equivalent of doing this: + @code + Type compareAndSetValue (Type newValue, Type valueToCompare) + { + Type oldValue = get(); + if (oldValue == valueToCompare) + set (newValue); + + return oldValue; + } + @endcode + + @returns the old value before it was changed. + @see compareAndSetBool + */ + inline Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept { return AtomicBase::compareAndSetValue (newValue, valueToCompare); } + + /** Implements a memory read/write barrier. */ + static inline void memoryBarrier() noexcept { AtomicBase::memoryBarrier (); } + }; + + #ifndef DOXYGEN + + //============================================================================== + // Internal implementation follows + //============================================================================== + template + class AtomicBase + { + public: + typedef typename AtomicHelpers::DiffTypeHelper::Type DiffType; + + inline AtomicBase() noexcept : value (0) {} + inline explicit AtomicBase (const Type v) noexcept : value (v) {} + inline AtomicBase (const AtomicBase& other) noexcept : value (other.get()) {} + Type get() const noexcept; + inline AtomicBase& operator= (const AtomicBase& other) noexcept { exchange (other.get()); return *this; } + inline AtomicBase& operator= (const Type newValue) noexcept { exchange (newValue); return *this; } + void set (Type newValue) noexcept { exchange (newValue); } + Type exchange (Type) noexcept; + bool compareAndSetBool (Type, Type) noexcept; + Type compareAndSetValue (Type, Type) noexcept; + static void memoryBarrier() noexcept; + + //============================================================================== + #if JUCE_64BIT + JUCE_ALIGN (8) + #else + JUCE_ALIGN (4) + #endif + + /** The raw value that this class operates on. + This is exposed publicly in case you need to manipulate it directly + for performance reasons. + */ + volatile Type value; + + protected: + template + static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; } + + static inline Type castFrom32Bit (int32 value) noexcept { return castTo (value); } + static inline Type castFrom64Bit (int64 value) noexcept { return castTo (value); } + static inline int32 castTo32Bit (Type value) noexcept { return castTo (value); } + static inline int64 castTo64Bit (Type value) noexcept { return castTo (value); } + + Type operator++ (int); // better to just use pre-increment with atomics.. + Type operator-- (int); + + /** This templated negate function will negate pointers as well as integers */ + template + inline ValueType negateValue (ValueType n) noexcept + { + return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n + : (sizeof (ValueType) == 2 ? (ValueType) -(short) n + : (sizeof (ValueType) == 4 ? (ValueType) -(int) n + : ((ValueType) -(int64) n))); + } + + /** This templated negate function will negate pointers as well as integers */ + template + inline PointerType* negateValue (PointerType* n) noexcept + { + return reinterpret_cast (-reinterpret_cast (n)); + } + }; + + //============================================================================== + // Specialisation for void* which does not include the pointer arithmetic + template <> + class Atomic : public AtomicBase + { + public: + inline Atomic() noexcept {} + inline explicit Atomic (void* const initialValue) noexcept : AtomicBase (initialValue) {} + inline Atomic (const Atomic& other) noexcept : AtomicBase (other) {} + inline void* get() const noexcept { return AtomicBase::get(); } + inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase::operator= (other); return *this; } + inline Atomic& operator= (void* const newValue) noexcept { AtomicBase::operator= (newValue); return *this; } + inline void set (void* newValue) noexcept { exchange (newValue); } + inline void* exchange (void* v) noexcept { return AtomicBase::exchange (v); } + inline bool compareAndSetBool (void* newValue, void* valueToCompare) noexcept { return AtomicBase::compareAndSetBool (newValue, valueToCompare); } + inline void* compareAndSetValue (void* newValue, void* valueToCompare) noexcept { return AtomicBase::compareAndSetValue (newValue, valueToCompare); } + static inline void memoryBarrier() noexcept { AtomicBase::memoryBarrier(); } + }; + + //============================================================================== + /* + The following code is in the header so that the atomics can be inlined where possible... + */ + #if JUCE_MAC && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)) + #define JUCE_ATOMICS_MAC_LEGACY 1 // Older OSX builds using gcc4.1 or earlier + #elif JUCE_GCC || JUCE_CLANG + #define JUCE_ATOMICS_GCC 1 // GCC with intrinsics + #if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 + #endif #endif - template - struct WindowsInterlockedHelpersBase - {}; - template - struct WindowsInterlockedHelpersBase + struct AtomicIncrementDecrement { - static inline Type exchange (volatile Type* value, Type other) noexcept + static inline Type inc (AtomicBase& a) noexcept { - return castFrom (juce_InterlockedExchange (reinterpret_cast (value), castTo (other))); + #if JUCE_ATOMICS_MAC_LEGACY + return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((volatile int32_t*) &a.value) + : (Type) OSAtomicIncrement64Barrier ((volatile int64_t*) &a.value); + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (& (a.value), (Type) 1) + : (Type) __sync_add_and_fetch ((int64_t*) & (a.value), 1); + #endif } - static inline Type add (volatile Type* value, Type other) noexcept + static inline Type dec (AtomicBase& a) noexcept { - return castFrom (juce_InterlockedExchangeAdd (reinterpret_cast (value), castTo (other)) + castTo (other)); + #if JUCE_ATOMICS_MAC_LEGACY + return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((volatile int32_t*) &a.value) + : (Type) OSAtomicDecrement64Barrier ((volatile int64_t*) &a.value); + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (& (a.value), (Type) -1) + : (Type) __sync_add_and_fetch ((int64_t*) & (a.value), -1); + #endif } - - static inline Type inc (volatile Type* value) noexcept - { - return castFrom (juce_InterlockedIncrement (reinterpret_cast (value))); - } - - static inline Type dec (volatile Type* value) noexcept - { - return castFrom (juce_InterlockedDecrement (reinterpret_cast (value))); - } - - static inline Type cmp (volatile Type* value, Type other, Type comparand) noexcept - { - return castFrom (juce_InterlockedCompareExchange (reinterpret_cast (value), castTo (other), castTo (comparand))); - } - - static inline Type castFrom (long value) { union { long in; Type out; } u; u.in = value; return u.out; } - static inline long castTo (Type value) { union { Type in; long out; } u; u.in = value; return u.out; } }; template - struct WindowsInterlockedHelpersBase + struct AtomicIncrementDecrement { - static inline Type exchange (volatile Type* value, Type other) noexcept - { - return castFrom (juce_InterlockedExchange64 (reinterpret_cast (value), castTo (other))); - } - - static inline Type add (volatile Type* value, Type other) noexcept - { - return castFrom (juce_InterlockedExchangeAdd64 (reinterpret_cast (value), castTo (other)) + castTo (other)); - } - - static inline Type inc (volatile Type* value) noexcept - { - return castFrom (juce_InterlockedIncrement64 (reinterpret_cast (value))); - } - - static inline Type dec (volatile Type* value) noexcept - { - return castFrom (juce_InterlockedDecrement64 (reinterpret_cast (value))); - } - - static inline Type cmp (volatile Type* value, Type other, Type comparand) noexcept - { - return castFrom (juce_InterlockedCompareExchange64 (reinterpret_cast (value), castTo (other), castTo (comparand))); - } - - static inline Type castFrom (__int64 value) { union { __int64 in; Type out; } u; u.in = value; return u.out; } - static inline __int64 castTo (Type value) { union { Type in; __int64 out; } u; u.in = value; return u.out; } + static inline Type* inc (Atomic& a) noexcept { return a.operator+= (1); } + static inline Type* dec (Atomic& a) noexcept { return a.operator-= (1); } }; + //============================================================================== template - struct WindowsInterlockedHelpers : WindowsInterlockedHelpersBase {}; -#endif - - -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4311) // (truncation warning) -#endif - -template -struct AtomicIncrementDecrement -{ - static inline Type inc (AtomicBase& a) noexcept - { - #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((volatile int32_t*) &a.value) - : (Type) OSAtomicIncrement64Barrier ((volatile int64_t*) &a.value); - #elif JUCE_ATOMICS_WINDOWS - return WindowsInterlockedHelpers::inc (&a.value); - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (& (a.value), (Type) 1) - : (Type) __sync_add_and_fetch ((int64_t*) & (a.value), 1); - #endif - } - - static inline Type dec (AtomicBase& a) noexcept - { - #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((volatile int32_t*) &a.value) - : (Type) OSAtomicDecrement64Barrier ((volatile int64_t*) &a.value); - #elif JUCE_ATOMICS_WINDOWS - return WindowsInterlockedHelpers::dec (&a.value); - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (& (a.value), (Type) -1) - : (Type) __sync_add_and_fetch ((int64_t*) & (a.value), -1); - #endif - } -}; - -template -struct AtomicIncrementDecrement -{ - static inline Type* inc (Atomic& a) noexcept { return a.operator+= (1); } - static inline Type* dec (Atomic& a) noexcept { return a.operator-= (1); } -}; - -//============================================================================== -template -inline Type AtomicBase::get() const noexcept -{ - #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (volatile int32_t*) &value)) - : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (volatile int64_t*) &value)); - #elif JUCE_ATOMICS_WINDOWS - return WindowsInterlockedHelpers::add (const_cast (&value), (Type) 0); - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) - : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); - #endif -} - -template -inline Type AtomicBase::exchange (const Type newValue) noexcept -{ - #if JUCE_ATOMICS_MAC_LEGACY || JUCE_ATOMICS_GCC - Type currentVal = value; - while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } - return currentVal; - #elif JUCE_ATOMICS_WINDOWS - return WindowsInterlockedHelpers::exchange (&value, newValue); - #endif -} - -template -inline Type Atomic::operator+= (const DiffType amountToAdd) noexcept -{ - Type amount = (Type() + amountToAdd); - - #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amount), (volatile int32_t*) &AtomicBase::value) - : (Type) OSAtomicAdd64Barrier ((int64_t) amount, (volatile int64_t*) &AtomicBase::value); - #elif JUCE_ATOMICS_WINDOWS - return WindowsInterlockedHelpers::add (& (AtomicBase::value), amount); - #elif JUCE_ATOMICS_GCC - return (Type) __sync_add_and_fetch (& (AtomicBase::value), amount); - #endif -} - -template -inline Type Atomic::operator-= (const DiffType amountToSubtract) noexcept -{ - return operator+= (AtomicBase::negateValue (amountToSubtract)); -} - -template -inline Type Atomic::operator++() noexcept { return AtomicIncrementDecrement::inc (*this); } - -template -inline Type Atomic::operator--() noexcept { return AtomicIncrementDecrement::dec (*this); } - -template -inline bool AtomicBase::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept -{ - #if JUCE_ATOMICS_MAC_LEGACY - return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (volatile int32_t*) &value) - : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (volatile int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS - return compareAndSetValue (newValue, valueToCompare) == valueToCompare; - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) - : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)); - #endif -} - -template -inline Type AtomicBase::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept -{ - #if JUCE_ATOMICS_MAC_LEGACY - for (;;) // Annoying workaround for only having a bool CAS operation.. - { - if (compareAndSetBool (newValue, valueToCompare)) - return valueToCompare; - - const Type result = value; - if (result != valueToCompare) - return result; - } - - #elif JUCE_ATOMICS_WINDOWS - return WindowsInterlockedHelpers::cmp (&value, newValue, valueToCompare); - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) - : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); - #endif -} - -template -inline void AtomicBase::memoryBarrier() noexcept -{ - #if JUCE_ATOMICS_MAC_LEGACY - OSMemoryBarrier(); - #elif JUCE_ATOMICS_GCC - __sync_synchronize(); - #elif JUCE_ATOMICS_WINDOWS - juce_MemoryBarrier(); - #endif -} - -#if JUCE_MSVC - #pragma warning (pop) -#endif + inline Type AtomicBase::get() const noexcept + { + #if JUCE_ATOMICS_MAC_LEGACY + return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (volatile int32_t*) &value)) + : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (volatile int64_t*) &value)); + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) + : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); + #endif + } + + template + inline Type AtomicBase::exchange (const Type newValue) noexcept + { + #if JUCE_ATOMICS_MAC_LEGACY || JUCE_ATOMICS_GCC + Type currentVal = value; + while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } + return currentVal; + #endif + } + + template + inline Type Atomic::operator+= (const DiffType amountToAdd) noexcept + { + Type amount = (Type() + amountToAdd); + + #if JUCE_ATOMICS_MAC_LEGACY + return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amount), (volatile int32_t*) &AtomicBase::value) + : (Type) OSAtomicAdd64Barrier ((int64_t) amount, (volatile int64_t*) &AtomicBase::value); + #elif JUCE_ATOMICS_GCC + return (Type) __sync_add_and_fetch (& (AtomicBase::value), amount); + #endif + } + + template + inline Type Atomic::operator-= (const DiffType amountToSubtract) noexcept + { + return operator+= (AtomicBase::negateValue (amountToSubtract)); + } + + template + inline Type Atomic::operator++() noexcept { return AtomicIncrementDecrement::inc (*this); } + + template + inline Type Atomic::operator--() noexcept { return AtomicIncrementDecrement::dec (*this); } + + template + inline bool AtomicBase::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept + { + #if JUCE_ATOMICS_MAC_LEGACY + return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (volatile int32_t*) &value) + : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (volatile int64_t*) &value); + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) + : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)); + #endif + } + + template + inline Type AtomicBase::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept + { + #if JUCE_ATOMICS_MAC_LEGACY + for (;;) // Annoying workaround for only having a bool CAS operation.. + { + if (compareAndSetBool (newValue, valueToCompare)) + return valueToCompare; + + const Type result = value; + if (result != valueToCompare) + return result; + } + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) + : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); + #endif + } + + template + inline void AtomicBase::memoryBarrier() noexcept + { + #if JUCE_ATOMICS_MAC_LEGACY + OSMemoryBarrier(); + #elif JUCE_ATOMICS_GCC + __sync_synchronize(); + #endif + } + + #endif + #endif diff --git a/modules/juce_core/system/juce_CompilerSupport.h b/modules/juce_core/system/juce_CompilerSupport.h index 6195e99e40..fb5d53be2f 100644 --- a/modules/juce_core/system/juce_CompilerSupport.h +++ b/modules/juce_core/system/juce_CompilerSupport.h @@ -153,3 +153,14 @@ #define override #endif #endif + +//============================================================================== +#if defined(_LIBCPP_VERSION) + #define JUCE_ATOMIC_AVAILABLE (_LIBCPP_VERSION >= 3700) +#elif defined (__GLIBCXX__) + #define JUCE_ATOMIC_AVAILABLE (__GLIBCXX__ >= 20130322) // GCC versions 4.8 and later +#elif defined (_MSC_VER) + #define JUCE_ATOMIC_AVAILABLE 1 // Visual Studio 2013 and later +#else + #define JUCE_ATOMIC_AVAILABLE 0 +#endif diff --git a/modules/juce_core/system/juce_StandardHeader.h b/modules/juce_core/system/juce_StandardHeader.h index baba6e5f32..bb974069b7 100644 --- a/modules/juce_core/system/juce_StandardHeader.h +++ b/modules/juce_core/system/juce_StandardHeader.h @@ -113,6 +113,11 @@ #include "../misc/juce_StdFunctionCompat.h" #endif +// Include std::atomic if it's supported by the compiler +#if JUCE_ATOMIC_AVAILABLE + #include +#endif + //============================================================================== // DLL building settings on Windows #if JUCE_MSVC diff --git a/modules/juce_core/threads/juce_Thread.cpp b/modules/juce_core/threads/juce_Thread.cpp index c0e30413d4..216249a85b 100644 --- a/modules/juce_core/threads/juce_Thread.cpp +++ b/modules/juce_core/threads/juce_Thread.cpp @@ -378,16 +378,12 @@ public: static void testFloat (UnitTest& test) { Atomic a, b; - a = (Type) 21; + a = (Type) 101; a.memoryBarrier(); /* These are some simple test cases to check the atomics - let me know if any of these assertions fail on your system! */ - test.expect (a.get() == (Type) 21); - test.expect (a.compareAndSetValue ((Type) 100, (Type) 50) == (Type) 21); - test.expect (a.get() == (Type) 21); - test.expect (a.compareAndSetValue ((Type) 101, a.get()) == (Type) 21); test.expect (a.get() == (Type) 101); test.expect (! a.compareAndSetBool ((Type) 300, (Type) 200)); test.expect (a.get() == (Type) 101);