| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- ///////////////////////////////////////////////////////////////////////////////
- // 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
- // VC++ yields spurious warnings about void* being cast to an integer type and vice-versa.
- // These warnings are baseless because we check for platform pointer size above.
- EA_DISABLE_VC_WARNING(4311 4312 4251)
- /// 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))); }
- };
- EA_RESTORE_VC_WARNING()
- } // namespace Thread
- } // namespace EA
- #endif // EATHREAD_EATHREAD_ATOMIC_H
|