First
This commit is contained in:
@@ -0,0 +1,480 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Defines functionality for thread-safe primitive operations.
|
||||
//
|
||||
// EAThread atomics do NOT imply the use of read/write barriers. This is
|
||||
// partly due to historical reasons and partly due to EAThread's internal
|
||||
// code being optimized for not using barriers.
|
||||
//
|
||||
// In future, we are considering migrating the atomics interface which
|
||||
// defaults atomics to use full read/write barriers while allowing users
|
||||
// to opt-out of full barrier usage. The new C++11 interface already provides
|
||||
// similar interfaces.
|
||||
//
|
||||
// http://en.cppreference.com/w/cpp/atomic/memory_order
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef EATHREAD_EATHREAD_ATOMIC_H
|
||||
#define EATHREAD_EATHREAD_ATOMIC_H
|
||||
|
||||
|
||||
#include <EABase/eabase.h>
|
||||
EA_DISABLE_ALL_VC_WARNINGS()
|
||||
#include <stddef.h>
|
||||
EA_RESTORE_ALL_VC_WARNINGS()
|
||||
#include <eathread/internal/config.h>
|
||||
#include <eathread/eathread.h>
|
||||
#include <eathread/eathread_sync.h>
|
||||
|
||||
|
||||
#if !EA_THREADS_AVAILABLE
|
||||
// Do nothing. Let the default implementation below be used.
|
||||
//#elif defined(EA_USE_CPP11_CONCURRENCY) && EA_USE_CPP11_CONCURRENCY
|
||||
// #include <eathread/cpp11/eathread_atomic_cpp11.h> // CPP11 atomics are currently broken and slow. To be renabled for other platforms when VS2013 released.
|
||||
#elif defined(EA_USE_COMMON_ATOMICINT_IMPLEMENTATION) && EA_USE_COMMON_ATOMICINT_IMPLEMENTATION
|
||||
#include <eathread/internal/eathread_atomic.h>
|
||||
#elif defined(EA_PLATFORM_APPLE)
|
||||
#include <eathread/apple/eathread_atomic_apple.h>
|
||||
#elif defined(EA_PROCESSOR_X86) || ((defined(EA_PLATFORM_WINRT) || defined(EA_PLATFORM_WINDOWS_PHONE)) && defined(EA_PROCESSOR_ARM))
|
||||
#include <eathread/x86/eathread_atomic_x86.h>
|
||||
#elif defined(EA_PROCESSOR_X86_64)
|
||||
#include <eathread/x86-64/eathread_atomic_x86-64.h>
|
||||
#elif defined(EA_PLATFORM_ANDROID)
|
||||
#if EATHREAD_C11_ATOMICS_AVAILABLE
|
||||
#include <eathread/android/eathread_atomic_android_c11.h> // Android API 21+ only support C11 atomics
|
||||
#else
|
||||
#include <eathread/android/eathread_atomic_android.h>
|
||||
#endif
|
||||
#elif defined(EA_COMPILER_GCC) || defined(CS_UNDEFINED_STRING)
|
||||
#include <eathread/gcc/eathread_atomic_gcc.h>
|
||||
#else
|
||||
#error Platform not supported yet.
|
||||
#endif
|
||||
|
||||
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
|
||||
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// EATHREAD_ATOMIC_128_SUPPORTED
|
||||
//
|
||||
// Defined as 0 or 1. Defined as 1 whenever possible for the given compiler/platform combination.
|
||||
// Defines if 128 bit atomic operations are supported.
|
||||
// Such operations are only ever supported on 64 bit platforms.
|
||||
//
|
||||
#ifndef EATHREAD_ATOMIC_128_SUPPORTED // If not defined by one of the above headers...
|
||||
#define EATHREAD_ATOMIC_128_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace Thread
|
||||
{
|
||||
enum Atomic64Implementation
|
||||
{
|
||||
kAtomic64Emulated,
|
||||
kAtomic64Native
|
||||
};
|
||||
|
||||
/// SetDoubleWordAtomicsImplementation
|
||||
/// Some platforms have multiple implementations, some of which support
|
||||
/// double word atomics and some that don't. For example, certain ARM
|
||||
/// processors will support the ldrexd/strexd atomic instructions but
|
||||
/// others will not.
|
||||
EATHREADLIB_API void SetAtomic64Implementation(Atomic64Implementation implementation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(EA_THREAD_ATOMIC_IMPLEMENTED) // If there wasn't a processor-specific version already defined...
|
||||
|
||||
// Fail the build if atomics aren't being defined for the given platform/compiler.
|
||||
// If we need to add an exception here, we can add an appropriate ifdef.
|
||||
static_assert(false, "atomic operations must be defined for this platform.");
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace Thread
|
||||
{
|
||||
/// Standalone atomic functions
|
||||
/// These act the same as the class functions below.
|
||||
/// The T return values are the previous value, except for the
|
||||
/// AtomicFetchSwap function which returns the swapped out value.
|
||||
///
|
||||
/// T AtomicGetValue(volatile T*);
|
||||
/// T AtomicGetValue(const volatile T*);
|
||||
/// void AtomicSetValue(volatile T*, T value);
|
||||
/// T AtomicFetchIncrement(volatile T*);
|
||||
/// T AtomicFetchDecrement(volatile T*);
|
||||
/// T AtomicFetchAdd(volatile T*, T value);
|
||||
/// T AtomicFetchSub(volatile T*, T value);
|
||||
/// T AtomicFetchOr(volatile T*, T value);
|
||||
/// T AtomicFetchAnd(volatile T*, T value);
|
||||
/// T AtomicFetchXor(volatile T*, T value);
|
||||
/// T AtomicFetchSwap(volatile T*, T value);
|
||||
/// T AtomicFetchSwapConditional(volatile T*, T value, T condition);
|
||||
/// bool AtomicSetValueConditional(volatile T*, T value, T condition);
|
||||
|
||||
|
||||
|
||||
/// class AtomicInt
|
||||
///
|
||||
/// Implements thread-safe access to an integer and primary operations on that integer.
|
||||
/// AtomicIntegers are commonly used as lightweight flags and signals between threads
|
||||
/// or as the synchronization object for spinlocks. Those familiar with the Win32 API
|
||||
/// will find that AtomicInt32 is essentially a platform independent interface to
|
||||
/// the Win32 InterlockedXXX family of functions. Those familiar with Linux may
|
||||
/// find that AtomicInt32 is essentially a platform independent interface to atomic_t
|
||||
/// functionality.
|
||||
///
|
||||
/// Note that the reference implementation defined here is itself not thread-safe.
|
||||
/// A thread-safe version requires platform-specific code.
|
||||
///
|
||||
/// Example usage
|
||||
/// AtomicInt32 i = 0;
|
||||
///
|
||||
/// ++i;
|
||||
/// i--;
|
||||
/// i += 7;
|
||||
/// i -= 3;
|
||||
/// i = 2;
|
||||
///
|
||||
/// int x = i.GetValue();
|
||||
/// i.Increment();
|
||||
/// bool oldValueWas6 = i.SetValueConditional(3, 6);
|
||||
/// i.Add(4);
|
||||
///
|
||||
template <class T>
|
||||
class EATHREADLIB_API AtomicInt
|
||||
{
|
||||
public:
|
||||
/// ThisType
|
||||
/// A typedef for this class type itself, for usage clarity.
|
||||
typedef AtomicInt<T> ThisType;
|
||||
|
||||
|
||||
/// ValueType
|
||||
/// A typedef for the basic object we work with.
|
||||
typedef T ValueType;
|
||||
|
||||
|
||||
/// AtomicInt
|
||||
/// Empty constructor. Intentionally leaves mValue in an unspecified state.
|
||||
/// This is done so that an AtomicInt acts like a standard built-in integer.
|
||||
AtomicInt()
|
||||
{}
|
||||
|
||||
|
||||
/// AtomicInt
|
||||
/// Constructs with an intial value.
|
||||
AtomicInt(ValueType n)
|
||||
: mValue(n) {}
|
||||
|
||||
|
||||
/// AtomicInt
|
||||
/// Copy ctor. Uses GetValue to read the value, and thus is synchronized.
|
||||
AtomicInt(const ThisType& x)
|
||||
: mValue(x.GetValue()) {}
|
||||
|
||||
|
||||
/// AtomicInt
|
||||
/// Assignment operator. Uses GetValue to read the value, and thus is synchronized.
|
||||
AtomicInt& operator=(const ThisType& x)
|
||||
{ mValue = x.GetValue(); return *this; }
|
||||
|
||||
|
||||
/// GetValue
|
||||
/// Safely gets the current value. A platform-specific version of
|
||||
/// this might need to do something more than just read the value.
|
||||
ValueType GetValue() const
|
||||
{ return mValue; }
|
||||
|
||||
|
||||
/// GetValueRaw
|
||||
/// "Unsafely" gets the current value. This is useful for algorithms
|
||||
/// that want to poll the value in a high performance way before
|
||||
/// reading or setting the value in a more costly thread-safe way.
|
||||
/// You should not use this function when attempting to do thread-safe
|
||||
/// atomic operations.
|
||||
ValueType GetValueRaw() const
|
||||
{ return mValue; }
|
||||
|
||||
|
||||
/// SetValue
|
||||
/// Safely sets a new value. Returns the old value. Note that due to
|
||||
/// expected multithreaded accesses, a call to GetValue after SetValue
|
||||
/// might return a different value then what was set with SetValue.
|
||||
/// This of course depends on your situation.
|
||||
ValueType SetValue(ValueType n)
|
||||
{
|
||||
const ValueType nOldValue(mValue);
|
||||
mValue = n;
|
||||
return nOldValue;
|
||||
}
|
||||
|
||||
|
||||
/// SetValueConditional
|
||||
/// Safely set the value to a new value if the original value is equal to
|
||||
/// a condition value. Returns true if the condition was met and the
|
||||
/// assignment occurred. The comparison and value setting are done as
|
||||
/// an atomic operation and thus another thread cannot intervene between
|
||||
/// the two as would be the case with simple C code.
|
||||
bool SetValueConditional(ValueType n, ValueType condition)
|
||||
{
|
||||
if(mValue == condition)
|
||||
{
|
||||
mValue = n;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Increment
|
||||
/// Safely increments the value. Returns the new value.
|
||||
/// This function acts the same as the C++ pre-increment operator.
|
||||
ValueType Increment()
|
||||
{ return ++mValue; }
|
||||
|
||||
|
||||
/// Decrement
|
||||
/// Safely decrements the value. Returns the new value.
|
||||
/// This function acts the same as the C++ pre-decrement operator.
|
||||
ValueType Decrement()
|
||||
{ return --mValue; }
|
||||
|
||||
|
||||
/// Add
|
||||
/// Safely adds a value, which can be negative. Returns the new value.
|
||||
/// You can implement subtraction with this function by using a negative argument.
|
||||
ValueType Add(ValueType n)
|
||||
{ return (mValue += n); }
|
||||
|
||||
|
||||
/// operators
|
||||
/// These allow an AtomicInt object to safely act like a built-in type.
|
||||
///
|
||||
/// Note: The operators for AtomicInt behaves differently than standard
|
||||
/// C++ operators in that it will always return a ValueType instead
|
||||
/// of a reference.
|
||||
///
|
||||
/// cast operator
|
||||
/// Returns the AtomicInt value as an integral type. This allows the
|
||||
/// AtomicInt to behave like a standard built-in integer type.
|
||||
operator const ValueType() const
|
||||
{ return mValue; }
|
||||
|
||||
/// operator =
|
||||
/// Assigns a new value and returns the value after the operation.
|
||||
///
|
||||
ValueType operator=(ValueType n)
|
||||
{
|
||||
mValue = n;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// pre-increment operator+=
|
||||
/// Adds a value to the AtomicInt and returns the value after the operation.
|
||||
///
|
||||
/// This function doesn't obey the C++ standard in that it does not return
|
||||
/// a reference, but rather the value of the AtomicInt after the
|
||||
/// operation is complete. It must be noted that this design is motivated by
|
||||
/// the fact that it is unsafe to rely on the returned value being equal to
|
||||
/// the previous value + n, as another thread might have modified the AtomicInt
|
||||
/// immediately after the subtraction operation. So rather than returning the
|
||||
/// reference of AtomicInt, the function returns a copy of the AtomicInt value
|
||||
/// used in the function.
|
||||
ValueType operator+=(ValueType n)
|
||||
{
|
||||
mValue += n;
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/// pre-increment operator-=
|
||||
/// Subtracts a value to the AtomicInt and returns the value after the operation.
|
||||
///
|
||||
/// This function doesn't obey the C++ standard in that it does not return
|
||||
// a reference, but rather the value of the AtomicInt after the
|
||||
/// operation is complete. It must be noted that this design is motivated by
|
||||
/// the fact that it is unsafe to rely on the returned value being equal to
|
||||
/// the previous value - n, as another thread might have modified the AtomicInt
|
||||
/// immediately after the subtraction operation. So rather than returning the
|
||||
/// reference of AtomicInt, the function returns a copy of the AtomicInt value
|
||||
/// used in the function.
|
||||
ValueType operator-=(ValueType n)
|
||||
{
|
||||
mValue -= n;
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/// pre-increment operator++
|
||||
/// Increments the AtomicInt.
|
||||
///
|
||||
/// This function doesn't obey the C++ standard in that it does not return
|
||||
// a reference, but rather the value of the AtomicInt after the
|
||||
/// operation is complete. It must be noted that this design is motivated by
|
||||
/// the fact that it is unsafe to rely on the returned value being equal to
|
||||
/// the previous value + 1, as another thread might have modified the AtomicInt
|
||||
/// immediately after the subtraction operation. So rather than returning the
|
||||
/// reference of AtomicInt, the function returns a copy of the AtomicInt value
|
||||
/// used in the function.
|
||||
ValueType operator++()
|
||||
{ return ++mValue; }
|
||||
|
||||
/// post-increment operator++
|
||||
/// Increments the AtomicInt and returns the value of the AtomicInt before
|
||||
/// the increment operation.
|
||||
///
|
||||
/// This function doesn't obey the C++ standard in that it does not return
|
||||
// a reference, but rather the value of the AtomicInt after the
|
||||
/// operation is complete. It must be noted that this design is motivated by
|
||||
/// the fact that it is unsafe to rely on the returned value being equal to
|
||||
/// the previous value, as another thread might have modified the AtomicInt
|
||||
/// immediately after the subtraction operation. So rather than returning the
|
||||
/// reference of AtomicInt, the function returns a copy of the AtomicInt value
|
||||
/// used in the function.
|
||||
ValueType operator++(int)
|
||||
{ return mValue++; }
|
||||
|
||||
/// pre-increment operator--
|
||||
/// Decrements the AtomicInt.
|
||||
///
|
||||
/// This function doesn't obey the C++ standard in that it does not return
|
||||
// a reference, but rather the value of the AtomicInt after the
|
||||
/// operation is complete. It must be noted that this design is motivated by
|
||||
/// the fact that it is unsafe to rely on the returned value being equal to
|
||||
/// the previous value - 1, as another thread might have modified the AtomicInt
|
||||
/// immediately after the subtraction operation. So rather than returning the
|
||||
/// reference of AtomicInt, the function returns a copy of the AtomicInt value
|
||||
/// used in the function.
|
||||
ValueType operator--()
|
||||
{ return --mValue; }
|
||||
|
||||
/// post-increment operator--
|
||||
/// Increments the AtomicInt and returns the value of the AtomicInt before
|
||||
/// the increment operation.
|
||||
///
|
||||
/// This function doesn't obey the C++ standard in that it does not return
|
||||
// a reference, but rather the value of the AtomicInt after the
|
||||
/// operation is complete. It must be noted that this design is motivated by
|
||||
/// the fact that it is unsafe to rely on the returned value being equal to
|
||||
/// the previous value, as another thread might have modified the AtomicInt
|
||||
/// immediately after the subtraction operation. So rather than returning the
|
||||
/// reference of AtomicInt, the function returns a copy of the AtomicInt value
|
||||
/// used in the function.
|
||||
ValueType operator--(int)
|
||||
{ return mValue--;}
|
||||
|
||||
protected:
|
||||
volatile ValueType mValue; /// May not be the same on all platforms.
|
||||
};
|
||||
|
||||
|
||||
} // namespace Thread
|
||||
|
||||
} // namespace EA
|
||||
|
||||
#endif // #if EA_THREAD_ATOMIC_IMPLEMENTED
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace EA
|
||||
{
|
||||
namespace Thread
|
||||
{
|
||||
|
||||
// Typedefs
|
||||
typedef AtomicInt<int32_t> AtomicInt32; /// int32_t atomic integer.
|
||||
typedef AtomicInt<uint32_t> AtomicUint32; /// uint32_t atomic integer.
|
||||
typedef AtomicInt<int64_t> AtomicInt64; /// int64_t atomic integer.
|
||||
typedef AtomicInt<uint64_t> AtomicUint64; /// uint64_t atomic integer.
|
||||
|
||||
#if !defined(EA_PLATFORM_WORD_SIZE) || (EA_PLATFORM_WORD_SIZE == 4)
|
||||
typedef AtomicInt32 AtomicIWord;
|
||||
typedef AtomicUint32 AtomicUWord;
|
||||
#else
|
||||
typedef AtomicInt64 AtomicIWord;
|
||||
typedef AtomicUint64 AtomicUWord;
|
||||
#endif
|
||||
|
||||
#if !defined(EA_PLATFORM_PTR_SIZE) || (EA_PLATFORM_PTR_SIZE == 4)
|
||||
typedef AtomicInt32 AtomicIntPtr;
|
||||
typedef AtomicUint32 AtomicUintPtr;
|
||||
#else
|
||||
typedef AtomicInt64 AtomicIntPtr;
|
||||
typedef AtomicUint64 AtomicUintPtr;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER // VC++ yields spurious warnings about void* being cast to an integer type and vice-versa.
|
||||
#pragma warning(push) // These warnings are baseless because we check for platform pointer size above.
|
||||
#pragma warning(disable: 4311 4312 4251)
|
||||
#endif
|
||||
|
||||
|
||||
/// class AtomicPointer
|
||||
///
|
||||
/// For simplicity of the current implementation, we simply have AtomicPointer map
|
||||
/// to AtomicInt32. This is reasonably safe because AtomicInt32 uses intptr_t
|
||||
/// as its ValueType and there are no foreseeble supported platforms in which
|
||||
/// intptr_t will not exist or be possible as a data type.
|
||||
///
|
||||
class EATHREADLIB_API AtomicPointer : public AtomicIntPtr
|
||||
{
|
||||
public:
|
||||
typedef void* PointerValueType;
|
||||
|
||||
AtomicPointer(void* p = NULL)
|
||||
: AtomicIntPtr(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p))) {}
|
||||
|
||||
AtomicPointer& operator=(void* p)
|
||||
{ AtomicIntPtr::operator=(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p))); return *this; }
|
||||
|
||||
operator const void*() const // It's debateable whether this should be supported.
|
||||
{ return (void*)AtomicIntPtr::GetValue(); }
|
||||
|
||||
void* GetValue() const
|
||||
{ return (void*)AtomicIntPtr::GetValue(); }
|
||||
|
||||
void* GetValueRaw() const
|
||||
{ return (void*)AtomicIntPtr::GetValueRaw(); }
|
||||
|
||||
void* SetValue(void* p)
|
||||
{ return (void*)AtomicIntPtr::SetValue(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p))); }
|
||||
|
||||
bool SetValueConditional(void* p, void* pCondition)
|
||||
{ return AtomicIntPtr::SetValueConditional(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p)), static_cast<ValueType>(reinterpret_cast<uintptr_t>(pCondition))); }
|
||||
};
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
} // namespace Thread
|
||||
|
||||
} // namespace EA
|
||||
|
||||
|
||||
#endif // EATHREAD_EATHREAD_ATOMIC_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user