mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Fixed a problem in a static assert in Atomic.
This commit is contained in:
parent
47698a86b1
commit
e6ae3f7aa7
1 changed files with 477 additions and 477 deletions
|
|
@ -1,477 +1,477 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
namespace AtomicHelpers
|
||||
{
|
||||
template <typename T> struct DiffTypeHelper { typedef T Type; };
|
||||
template <typename T> struct DiffTypeHelper<T*> { typedef std::ptrdiff_t Type; };
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JUCE_ATOMIC_AVAILABLE
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple wrapper around std::atomic.
|
||||
*/
|
||||
template <typename Type>
|
||||
struct Atomic
|
||||
{
|
||||
typedef typename AtomicHelpers::DiffTypeHelper<Type>::Type DiffType;
|
||||
|
||||
/** Creates a new value, initialised to zero. */
|
||||
Atomic() noexcept : value (0) {}
|
||||
|
||||
/** Creates a new value, with a given initial value. */
|
||||
Atomic (const Type initialValue) noexcept : value (initialValue) {}
|
||||
|
||||
/** Copies another value (atomically). */
|
||||
Atomic (const Atomic& other) noexcept : value (other.get()) {}
|
||||
|
||||
/** Destructor. */
|
||||
~Atomic() noexcept
|
||||
{
|
||||
#if __cpp_lib_atomic_is_always_lock_free
|
||||
static_assert (std::atomic<Type>::is_always_lock_free(),
|
||||
"This class can only be used for lock-free types");
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Atomically reads and returns the current value. */
|
||||
Type get() const noexcept { return value.load(); }
|
||||
|
||||
/** Atomically sets the current value. */
|
||||
void set (Type newValue) noexcept { value = newValue; }
|
||||
|
||||
/** Atomically sets the current value, returning the value that was replaced. */
|
||||
Type exchange (Type newValue) noexcept { return value.exchange (newValue); }
|
||||
|
||||
/** 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
|
||||
|
||||
Internally, this method calls std::atomic::compare_exchange_strong with
|
||||
memory_order_seq_cst (the strictest std::memory_order).
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
Atomic<Type>& operator= (const Atomic& other) noexcept
|
||||
{
|
||||
value = other.value.load();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
Atomic<Type>& operator= (const Type newValue) noexcept
|
||||
{
|
||||
value = newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Atomically adds a number to this value, returning the new value. */
|
||||
Type operator+= (DiffType amountToAdd) noexcept { return value += amountToAdd; }
|
||||
|
||||
/** Atomically subtracts a number from this value, returning the new value. */
|
||||
Type operator-= (DiffType amountToSubtract) noexcept { return value -= amountToSubtract; }
|
||||
|
||||
/** Atomically increments this value, returning the new value. */
|
||||
Type operator++() noexcept { return ++value; }
|
||||
|
||||
/** Atomically decrements this value, returning the new value. */
|
||||
Type operator--() noexcept { return --value; }
|
||||
|
||||
/** Implements a memory read/write barrier.
|
||||
|
||||
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); }
|
||||
|
||||
/** The std::atomic object that this class operates on. */
|
||||
std::atomic<Type> value;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// This method has been deprecated as there is no equivalent method in std::atomic.
|
||||
JUCE_DEPRECATED (Type compareAndSetValue (Type, Type) noexcept);
|
||||
#endif
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#ifndef DOXYGEN
|
||||
template <typename Type> 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 <typename Type>
|
||||
class Atomic : public AtomicBase<Type>
|
||||
{
|
||||
public:
|
||||
/** Resulting type when subtracting the underlying Type. */
|
||||
typedef typename AtomicBase<Type>::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<Type> (initialValue) {}
|
||||
|
||||
/** Copies another value (atomically). */
|
||||
inline Atomic (const Atomic& other) noexcept : AtomicBase<Type> (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<Type>::get(); }
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase<Type>::operator= (other); return *this; }
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
inline Atomic& operator= (const Type newValue) noexcept { AtomicBase<Type>::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<Type>::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<Type>::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<Type>::compareAndSetValue (newValue, valueToCompare); }
|
||||
|
||||
/** Implements a memory read/write barrier. */
|
||||
static inline void memoryBarrier() noexcept { AtomicBase<Type>::memoryBarrier (); }
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
// Internal implementation follows
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
class AtomicBase
|
||||
{
|
||||
public:
|
||||
typedef typename AtomicHelpers::DiffTypeHelper<Type>::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<Type>& 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 <typename Dest, typename Source>
|
||||
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 <Type, int32> (value); }
|
||||
static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); }
|
||||
static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); }
|
||||
static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (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 <typename ValueType>
|
||||
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 <typename PointerType>
|
||||
inline PointerType* negateValue (PointerType* n) noexcept
|
||||
{
|
||||
return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Specialisation for void* which does not include the pointer arithmetic
|
||||
template <>
|
||||
class Atomic<void*> : public AtomicBase<void*>
|
||||
{
|
||||
public:
|
||||
inline Atomic() noexcept {}
|
||||
inline explicit Atomic (void* const initialValue) noexcept : AtomicBase<void*> (initialValue) {}
|
||||
inline Atomic (const Atomic<void*>& other) noexcept : AtomicBase<void*> (other) {}
|
||||
inline void* get() const noexcept { return AtomicBase<void*>::get(); }
|
||||
inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase<void*>::operator= (other); return *this; }
|
||||
inline Atomic& operator= (void* const newValue) noexcept { AtomicBase<void*>::operator= (newValue); return *this; }
|
||||
inline void set (void* newValue) noexcept { exchange (newValue); }
|
||||
inline void* exchange (void* v) noexcept { return AtomicBase<void*>::exchange (v); }
|
||||
inline bool compareAndSetBool (void* newValue, void* valueToCompare) noexcept { return AtomicBase<void*>::compareAndSetBool (newValue, valueToCompare); }
|
||||
inline void* compareAndSetValue (void* newValue, void* valueToCompare) noexcept { return AtomicBase<void*>::compareAndSetValue (newValue, valueToCompare); }
|
||||
static inline void memoryBarrier() noexcept { AtomicBase<void*>::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 <typename Type>
|
||||
struct AtomicIncrementDecrement
|
||||
{
|
||||
static inline Type inc (AtomicBase<Type>& 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_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<Type>& 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_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 <typename Type>
|
||||
struct AtomicIncrementDecrement<Type*>
|
||||
{
|
||||
static inline Type* inc (Atomic<Type*>& a) noexcept { return a.operator+= (1); }
|
||||
static inline Type* dec (Atomic<Type*>& a) noexcept { return a.operator-= (1); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
inline Type AtomicBase<Type>::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 <typename Type>
|
||||
inline Type AtomicBase<Type>::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 <typename Type>
|
||||
inline Type Atomic<Type>::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<Type>::value)
|
||||
: (Type) OSAtomicAdd64Barrier ((int64_t) amount, (volatile int64_t*) &AtomicBase<Type>::value);
|
||||
#elif JUCE_ATOMICS_GCC
|
||||
return (Type) __sync_add_and_fetch (& (AtomicBase<Type>::value), amount);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator-= (const DiffType amountToSubtract) noexcept
|
||||
{
|
||||
return operator+= (AtomicBase<Type>::negateValue (amountToSubtract));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator++() noexcept { return AtomicIncrementDecrement<Type>::inc (*this); }
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator--() noexcept { return AtomicIncrementDecrement<Type>::dec (*this); }
|
||||
|
||||
template <typename Type>
|
||||
inline bool AtomicBase<Type>::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 <typename Type>
|
||||
inline Type AtomicBase<Type>::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 <typename Type>
|
||||
inline void AtomicBase<Type>::memoryBarrier() noexcept
|
||||
{
|
||||
#if JUCE_ATOMICS_MAC_LEGACY
|
||||
OSMemoryBarrier();
|
||||
#elif JUCE_ATOMICS_GCC
|
||||
__sync_synchronize();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
namespace AtomicHelpers
|
||||
{
|
||||
template <typename T> struct DiffTypeHelper { typedef T Type; };
|
||||
template <typename T> struct DiffTypeHelper<T*> { typedef std::ptrdiff_t Type; };
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JUCE_ATOMIC_AVAILABLE
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple wrapper around std::atomic.
|
||||
*/
|
||||
template <typename Type>
|
||||
struct Atomic
|
||||
{
|
||||
typedef typename AtomicHelpers::DiffTypeHelper<Type>::Type DiffType;
|
||||
|
||||
/** Creates a new value, initialised to zero. */
|
||||
Atomic() noexcept : value (0) {}
|
||||
|
||||
/** Creates a new value, with a given initial value. */
|
||||
Atomic (Type initialValue) noexcept : value (initialValue) {}
|
||||
|
||||
/** Copies another value (atomically). */
|
||||
Atomic (const Atomic& other) noexcept : value (other.get()) {}
|
||||
|
||||
/** Destructor. */
|
||||
~Atomic() noexcept
|
||||
{
|
||||
#if __cpp_lib_atomic_is_always_lock_free
|
||||
static_assert (std::atomic<Type>::is_always_lock_free,
|
||||
"This class can only be used for lock-free types");
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Atomically reads and returns the current value. */
|
||||
Type get() const noexcept { return value.load(); }
|
||||
|
||||
/** Atomically sets the current value. */
|
||||
void set (Type newValue) noexcept { value = newValue; }
|
||||
|
||||
/** Atomically sets the current value, returning the value that was replaced. */
|
||||
Type exchange (Type newValue) noexcept { return value.exchange (newValue); }
|
||||
|
||||
/** 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
|
||||
|
||||
Internally, this method calls std::atomic::compare_exchange_strong with
|
||||
memory_order_seq_cst (the strictest std::memory_order).
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
Atomic<Type>& operator= (const Atomic& other) noexcept
|
||||
{
|
||||
value = other.value.load();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
Atomic<Type>& operator= (Type newValue) noexcept
|
||||
{
|
||||
value = newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Atomically adds a number to this value, returning the new value. */
|
||||
Type operator+= (DiffType amountToAdd) noexcept { return value += amountToAdd; }
|
||||
|
||||
/** Atomically subtracts a number from this value, returning the new value. */
|
||||
Type operator-= (DiffType amountToSubtract) noexcept { return value -= amountToSubtract; }
|
||||
|
||||
/** Atomically increments this value, returning the new value. */
|
||||
Type operator++() noexcept { return ++value; }
|
||||
|
||||
/** Atomically decrements this value, returning the new value. */
|
||||
Type operator--() noexcept { return --value; }
|
||||
|
||||
/** Implements a memory read/write barrier.
|
||||
|
||||
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); }
|
||||
|
||||
/** The std::atomic object that this class operates on. */
|
||||
std::atomic<Type> value;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
// This method has been deprecated as there is no equivalent method in std::atomic.
|
||||
JUCE_DEPRECATED (Type compareAndSetValue (Type, Type) noexcept);
|
||||
#endif
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#ifndef DOXYGEN
|
||||
template <typename Type> 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 <typename Type>
|
||||
class Atomic : public AtomicBase<Type>
|
||||
{
|
||||
public:
|
||||
/** Resulting type when subtracting the underlying Type. */
|
||||
typedef typename AtomicBase<Type>::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<Type> (initialValue) {}
|
||||
|
||||
/** Copies another value (atomically). */
|
||||
inline Atomic (const Atomic& other) noexcept : AtomicBase<Type> (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<Type>::get(); }
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase<Type>::operator= (other); return *this; }
|
||||
|
||||
/** Copies another value into this one (atomically). */
|
||||
inline Atomic& operator= (const Type newValue) noexcept { AtomicBase<Type>::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<Type>::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<Type>::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<Type>::compareAndSetValue (newValue, valueToCompare); }
|
||||
|
||||
/** Implements a memory read/write barrier. */
|
||||
static inline void memoryBarrier() noexcept { AtomicBase<Type>::memoryBarrier (); }
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
// Internal implementation follows
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
class AtomicBase
|
||||
{
|
||||
public:
|
||||
typedef typename AtomicHelpers::DiffTypeHelper<Type>::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<Type>& 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 <typename Dest, typename Source>
|
||||
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 <Type, int32> (value); }
|
||||
static inline Type castFrom64Bit (int64 value) noexcept { return castTo <Type, int64> (value); }
|
||||
static inline int32 castTo32Bit (Type value) noexcept { return castTo <int32, Type> (value); }
|
||||
static inline int64 castTo64Bit (Type value) noexcept { return castTo <int64, Type> (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 <typename ValueType>
|
||||
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 <typename PointerType>
|
||||
inline PointerType* negateValue (PointerType* n) noexcept
|
||||
{
|
||||
return reinterpret_cast<PointerType*> (-reinterpret_cast<pointer_sized_int> (n));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Specialisation for void* which does not include the pointer arithmetic
|
||||
template <>
|
||||
class Atomic<void*> : public AtomicBase<void*>
|
||||
{
|
||||
public:
|
||||
inline Atomic() noexcept {}
|
||||
inline explicit Atomic (void* const initialValue) noexcept : AtomicBase<void*> (initialValue) {}
|
||||
inline Atomic (const Atomic<void*>& other) noexcept : AtomicBase<void*> (other) {}
|
||||
inline void* get() const noexcept { return AtomicBase<void*>::get(); }
|
||||
inline Atomic& operator= (const Atomic& other) noexcept { AtomicBase<void*>::operator= (other); return *this; }
|
||||
inline Atomic& operator= (void* const newValue) noexcept { AtomicBase<void*>::operator= (newValue); return *this; }
|
||||
inline void set (void* newValue) noexcept { exchange (newValue); }
|
||||
inline void* exchange (void* v) noexcept { return AtomicBase<void*>::exchange (v); }
|
||||
inline bool compareAndSetBool (void* newValue, void* valueToCompare) noexcept { return AtomicBase<void*>::compareAndSetBool (newValue, valueToCompare); }
|
||||
inline void* compareAndSetValue (void* newValue, void* valueToCompare) noexcept { return AtomicBase<void*>::compareAndSetValue (newValue, valueToCompare); }
|
||||
static inline void memoryBarrier() noexcept { AtomicBase<void*>::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 <typename Type>
|
||||
struct AtomicIncrementDecrement
|
||||
{
|
||||
static inline Type inc (AtomicBase<Type>& 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_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<Type>& 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_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 <typename Type>
|
||||
struct AtomicIncrementDecrement<Type*>
|
||||
{
|
||||
static inline Type* inc (Atomic<Type*>& a) noexcept { return a.operator+= (1); }
|
||||
static inline Type* dec (Atomic<Type*>& a) noexcept { return a.operator-= (1); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
inline Type AtomicBase<Type>::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 <typename Type>
|
||||
inline Type AtomicBase<Type>::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 <typename Type>
|
||||
inline Type Atomic<Type>::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<Type>::value)
|
||||
: (Type) OSAtomicAdd64Barrier ((int64_t) amount, (volatile int64_t*) &AtomicBase<Type>::value);
|
||||
#elif JUCE_ATOMICS_GCC
|
||||
return (Type) __sync_add_and_fetch (& (AtomicBase<Type>::value), amount);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator-= (const DiffType amountToSubtract) noexcept
|
||||
{
|
||||
return operator+= (AtomicBase<Type>::negateValue (amountToSubtract));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator++() noexcept { return AtomicIncrementDecrement<Type>::inc (*this); }
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator--() noexcept { return AtomicIncrementDecrement<Type>::dec (*this); }
|
||||
|
||||
template <typename Type>
|
||||
inline bool AtomicBase<Type>::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 <typename Type>
|
||||
inline Type AtomicBase<Type>::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 <typename Type>
|
||||
inline void AtomicBase<Type>::memoryBarrier() noexcept
|
||||
{
|
||||
#if JUCE_ATOMICS_MAC_LEGACY
|
||||
OSMemoryBarrier();
|
||||
#elif JUCE_ATOMICS_GCC
|
||||
__sync_synchronize();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue