eathread_atomic.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. /////////////////////////////////////////////////////////////////////////////
  5. // Defines functionality for thread-safe primitive operations.
  6. //
  7. // EAThread atomics do NOT imply the use of read/write barriers. This is
  8. // partly due to historical reasons and partly due to EAThread's internal
  9. // code being optimized for not using barriers.
  10. //
  11. // In future, we are considering migrating the atomics interface which
  12. // defaults atomics to use full read/write barriers while allowing users
  13. // to opt-out of full barrier usage. The new C++11 interface already provides
  14. // similar interfaces.
  15. //
  16. // http://en.cppreference.com/w/cpp/atomic/memory_order
  17. /////////////////////////////////////////////////////////////////////////////
  18. #ifndef EATHREAD_EATHREAD_ATOMIC_H
  19. #define EATHREAD_EATHREAD_ATOMIC_H
  20. #include <EABase/eabase.h>
  21. EA_DISABLE_ALL_VC_WARNINGS()
  22. #include <stddef.h>
  23. EA_RESTORE_ALL_VC_WARNINGS()
  24. #include <eathread/internal/config.h>
  25. #include <eathread/eathread.h>
  26. #include <eathread/eathread_sync.h>
  27. #if !EA_THREADS_AVAILABLE
  28. // Do nothing. Let the default implementation below be used.
  29. //#elif defined(EA_USE_CPP11_CONCURRENCY) && EA_USE_CPP11_CONCURRENCY
  30. // #include <eathread/cpp11/eathread_atomic_cpp11.h> // CPP11 atomics are currently broken and slow. To be renabled for other platforms when VS2013 released.
  31. #elif defined(EA_USE_COMMON_ATOMICINT_IMPLEMENTATION) && EA_USE_COMMON_ATOMICINT_IMPLEMENTATION
  32. #include <eathread/internal/eathread_atomic.h>
  33. #elif defined(EA_PLATFORM_APPLE)
  34. #include <eathread/apple/eathread_atomic_apple.h>
  35. #elif defined(EA_PROCESSOR_X86) || ((defined(EA_PLATFORM_WINRT) || defined(EA_PLATFORM_WINDOWS_PHONE)) && defined(EA_PROCESSOR_ARM))
  36. #include <eathread/x86/eathread_atomic_x86.h>
  37. #elif defined(EA_PROCESSOR_X86_64)
  38. #include <eathread/x86-64/eathread_atomic_x86-64.h>
  39. #elif defined(EA_PLATFORM_ANDROID)
  40. #if EATHREAD_C11_ATOMICS_AVAILABLE
  41. #include <eathread/android/eathread_atomic_android_c11.h> // Android API 21+ only support C11 atomics
  42. #else
  43. #include <eathread/android/eathread_atomic_android.h>
  44. #endif
  45. #elif defined(EA_COMPILER_GCC) || defined(CS_UNDEFINED_STRING)
  46. #include <eathread/gcc/eathread_atomic_gcc.h>
  47. #else
  48. #error Platform not supported yet.
  49. #endif
  50. #if defined(EA_PRAGMA_ONCE_SUPPORTED)
  51. #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.
  52. #endif
  53. // EATHREAD_ATOMIC_128_SUPPORTED
  54. //
  55. // Defined as 0 or 1. Defined as 1 whenever possible for the given compiler/platform combination.
  56. // Defines if 128 bit atomic operations are supported.
  57. // Such operations are only ever supported on 64 bit platforms.
  58. //
  59. #ifndef EATHREAD_ATOMIC_128_SUPPORTED // If not defined by one of the above headers...
  60. #define EATHREAD_ATOMIC_128_SUPPORTED 0
  61. #endif
  62. namespace EA
  63. {
  64. namespace Thread
  65. {
  66. enum Atomic64Implementation
  67. {
  68. kAtomic64Emulated,
  69. kAtomic64Native
  70. };
  71. /// SetDoubleWordAtomicsImplementation
  72. /// Some platforms have multiple implementations, some of which support
  73. /// double word atomics and some that don't. For example, certain ARM
  74. /// processors will support the ldrexd/strexd atomic instructions but
  75. /// others will not.
  76. EATHREADLIB_API void SetAtomic64Implementation(Atomic64Implementation implementation);
  77. }
  78. }
  79. #if !defined(EA_THREAD_ATOMIC_IMPLEMENTED) // If there wasn't a processor-specific version already defined...
  80. // Fail the build if atomics aren't being defined for the given platform/compiler.
  81. // If we need to add an exception here, we can add an appropriate ifdef.
  82. static_assert(false, "atomic operations must be defined for this platform.");
  83. namespace EA
  84. {
  85. namespace Thread
  86. {
  87. /// Standalone atomic functions
  88. /// These act the same as the class functions below.
  89. /// The T return values are the previous value, except for the
  90. /// AtomicFetchSwap function which returns the swapped out value.
  91. ///
  92. /// T AtomicGetValue(volatile T*);
  93. /// T AtomicGetValue(const volatile T*);
  94. /// void AtomicSetValue(volatile T*, T value);
  95. /// T AtomicFetchIncrement(volatile T*);
  96. /// T AtomicFetchDecrement(volatile T*);
  97. /// T AtomicFetchAdd(volatile T*, T value);
  98. /// T AtomicFetchSub(volatile T*, T value);
  99. /// T AtomicFetchOr(volatile T*, T value);
  100. /// T AtomicFetchAnd(volatile T*, T value);
  101. /// T AtomicFetchXor(volatile T*, T value);
  102. /// T AtomicFetchSwap(volatile T*, T value);
  103. /// T AtomicFetchSwapConditional(volatile T*, T value, T condition);
  104. /// bool AtomicSetValueConditional(volatile T*, T value, T condition);
  105. /// class AtomicInt
  106. ///
  107. /// Implements thread-safe access to an integer and primary operations on that integer.
  108. /// AtomicIntegers are commonly used as lightweight flags and signals between threads
  109. /// or as the synchronization object for spinlocks. Those familiar with the Win32 API
  110. /// will find that AtomicInt32 is essentially a platform independent interface to
  111. /// the Win32 InterlockedXXX family of functions. Those familiar with Linux may
  112. /// find that AtomicInt32 is essentially a platform independent interface to atomic_t
  113. /// functionality.
  114. ///
  115. /// Note that the reference implementation defined here is itself not thread-safe.
  116. /// A thread-safe version requires platform-specific code.
  117. ///
  118. /// Example usage
  119. /// AtomicInt32 i = 0;
  120. ///
  121. /// ++i;
  122. /// i--;
  123. /// i += 7;
  124. /// i -= 3;
  125. /// i = 2;
  126. ///
  127. /// int x = i.GetValue();
  128. /// i.Increment();
  129. /// bool oldValueWas6 = i.SetValueConditional(3, 6);
  130. /// i.Add(4);
  131. ///
  132. template <class T>
  133. class EATHREADLIB_API AtomicInt
  134. {
  135. public:
  136. /// ThisType
  137. /// A typedef for this class type itself, for usage clarity.
  138. typedef AtomicInt<T> ThisType;
  139. /// ValueType
  140. /// A typedef for the basic object we work with.
  141. typedef T ValueType;
  142. /// AtomicInt
  143. /// Empty constructor. Intentionally leaves mValue in an unspecified state.
  144. /// This is done so that an AtomicInt acts like a standard built-in integer.
  145. AtomicInt()
  146. {}
  147. /// AtomicInt
  148. /// Constructs with an intial value.
  149. AtomicInt(ValueType n)
  150. : mValue(n) {}
  151. /// AtomicInt
  152. /// Copy ctor. Uses GetValue to read the value, and thus is synchronized.
  153. AtomicInt(const ThisType& x)
  154. : mValue(x.GetValue()) {}
  155. /// AtomicInt
  156. /// Assignment operator. Uses GetValue to read the value, and thus is synchronized.
  157. AtomicInt& operator=(const ThisType& x)
  158. { mValue = x.GetValue(); return *this; }
  159. /// GetValue
  160. /// Safely gets the current value. A platform-specific version of
  161. /// this might need to do something more than just read the value.
  162. ValueType GetValue() const
  163. { return mValue; }
  164. /// GetValueRaw
  165. /// "Unsafely" gets the current value. This is useful for algorithms
  166. /// that want to poll the value in a high performance way before
  167. /// reading or setting the value in a more costly thread-safe way.
  168. /// You should not use this function when attempting to do thread-safe
  169. /// atomic operations.
  170. ValueType GetValueRaw() const
  171. { return mValue; }
  172. /// SetValue
  173. /// Safely sets a new value. Returns the old value. Note that due to
  174. /// expected multithreaded accesses, a call to GetValue after SetValue
  175. /// might return a different value then what was set with SetValue.
  176. /// This of course depends on your situation.
  177. ValueType SetValue(ValueType n)
  178. {
  179. const ValueType nOldValue(mValue);
  180. mValue = n;
  181. return nOldValue;
  182. }
  183. /// SetValueConditional
  184. /// Safely set the value to a new value if the original value is equal to
  185. /// a condition value. Returns true if the condition was met and the
  186. /// assignment occurred. The comparison and value setting are done as
  187. /// an atomic operation and thus another thread cannot intervene between
  188. /// the two as would be the case with simple C code.
  189. bool SetValueConditional(ValueType n, ValueType condition)
  190. {
  191. if(mValue == condition)
  192. {
  193. mValue = n;
  194. return true;
  195. }
  196. return false;
  197. }
  198. /// Increment
  199. /// Safely increments the value. Returns the new value.
  200. /// This function acts the same as the C++ pre-increment operator.
  201. ValueType Increment()
  202. { return ++mValue; }
  203. /// Decrement
  204. /// Safely decrements the value. Returns the new value.
  205. /// This function acts the same as the C++ pre-decrement operator.
  206. ValueType Decrement()
  207. { return --mValue; }
  208. /// Add
  209. /// Safely adds a value, which can be negative. Returns the new value.
  210. /// You can implement subtraction with this function by using a negative argument.
  211. ValueType Add(ValueType n)
  212. { return (mValue += n); }
  213. /// operators
  214. /// These allow an AtomicInt object to safely act like a built-in type.
  215. ///
  216. /// Note: The operators for AtomicInt behaves differently than standard
  217. /// C++ operators in that it will always return a ValueType instead
  218. /// of a reference.
  219. ///
  220. /// cast operator
  221. /// Returns the AtomicInt value as an integral type. This allows the
  222. /// AtomicInt to behave like a standard built-in integer type.
  223. operator const ValueType() const
  224. { return mValue; }
  225. /// operator =
  226. /// Assigns a new value and returns the value after the operation.
  227. ///
  228. ValueType operator=(ValueType n)
  229. {
  230. mValue = n;
  231. return n;
  232. }
  233. /// pre-increment operator+=
  234. /// Adds a value to the AtomicInt and returns the value after the operation.
  235. ///
  236. /// This function doesn't obey the C++ standard in that it does not return
  237. /// a reference, but rather the value of the AtomicInt after the
  238. /// operation is complete. It must be noted that this design is motivated by
  239. /// the fact that it is unsafe to rely on the returned value being equal to
  240. /// the previous value + n, as another thread might have modified the AtomicInt
  241. /// immediately after the subtraction operation. So rather than returning the
  242. /// reference of AtomicInt, the function returns a copy of the AtomicInt value
  243. /// used in the function.
  244. ValueType operator+=(ValueType n)
  245. {
  246. mValue += n;
  247. return mValue;
  248. }
  249. /// pre-increment operator-=
  250. /// Subtracts a value to the AtomicInt and returns the value after the operation.
  251. ///
  252. /// This function doesn't obey the C++ standard in that it does not return
  253. // a reference, but rather the value of the AtomicInt after the
  254. /// operation is complete. It must be noted that this design is motivated by
  255. /// the fact that it is unsafe to rely on the returned value being equal to
  256. /// the previous value - n, as another thread might have modified the AtomicInt
  257. /// immediately after the subtraction operation. So rather than returning the
  258. /// reference of AtomicInt, the function returns a copy of the AtomicInt value
  259. /// used in the function.
  260. ValueType operator-=(ValueType n)
  261. {
  262. mValue -= n;
  263. return mValue;
  264. }
  265. /// pre-increment operator++
  266. /// Increments the AtomicInt.
  267. ///
  268. /// This function doesn't obey the C++ standard in that it does not return
  269. // a reference, but rather the value of the AtomicInt after the
  270. /// operation is complete. It must be noted that this design is motivated by
  271. /// the fact that it is unsafe to rely on the returned value being equal to
  272. /// the previous value + 1, as another thread might have modified the AtomicInt
  273. /// immediately after the subtraction operation. So rather than returning the
  274. /// reference of AtomicInt, the function returns a copy of the AtomicInt value
  275. /// used in the function.
  276. ValueType operator++()
  277. { return ++mValue; }
  278. /// post-increment operator++
  279. /// Increments the AtomicInt and returns the value of the AtomicInt before
  280. /// the increment operation.
  281. ///
  282. /// This function doesn't obey the C++ standard in that it does not return
  283. // a reference, but rather the value of the AtomicInt after the
  284. /// operation is complete. It must be noted that this design is motivated by
  285. /// the fact that it is unsafe to rely on the returned value being equal to
  286. /// the previous value, as another thread might have modified the AtomicInt
  287. /// immediately after the subtraction operation. So rather than returning the
  288. /// reference of AtomicInt, the function returns a copy of the AtomicInt value
  289. /// used in the function.
  290. ValueType operator++(int)
  291. { return mValue++; }
  292. /// pre-increment operator--
  293. /// Decrements the AtomicInt.
  294. ///
  295. /// This function doesn't obey the C++ standard in that it does not return
  296. // a reference, but rather the value of the AtomicInt after the
  297. /// operation is complete. It must be noted that this design is motivated by
  298. /// the fact that it is unsafe to rely on the returned value being equal to
  299. /// the previous value - 1, as another thread might have modified the AtomicInt
  300. /// immediately after the subtraction operation. So rather than returning the
  301. /// reference of AtomicInt, the function returns a copy of the AtomicInt value
  302. /// used in the function.
  303. ValueType operator--()
  304. { return --mValue; }
  305. /// post-increment operator--
  306. /// Increments the AtomicInt and returns the value of the AtomicInt before
  307. /// the increment operation.
  308. ///
  309. /// This function doesn't obey the C++ standard in that it does not return
  310. // a reference, but rather the value of the AtomicInt after the
  311. /// operation is complete. It must be noted that this design is motivated by
  312. /// the fact that it is unsafe to rely on the returned value being equal to
  313. /// the previous value, as another thread might have modified the AtomicInt
  314. /// immediately after the subtraction operation. So rather than returning the
  315. /// reference of AtomicInt, the function returns a copy of the AtomicInt value
  316. /// used in the function.
  317. ValueType operator--(int)
  318. { return mValue--;}
  319. protected:
  320. volatile ValueType mValue; /// May not be the same on all platforms.
  321. };
  322. } // namespace Thread
  323. } // namespace EA
  324. #endif // #if EA_THREAD_ATOMIC_IMPLEMENTED
  325. namespace EA
  326. {
  327. namespace Thread
  328. {
  329. // Typedefs
  330. typedef AtomicInt<int32_t> AtomicInt32; /// int32_t atomic integer.
  331. typedef AtomicInt<uint32_t> AtomicUint32; /// uint32_t atomic integer.
  332. typedef AtomicInt<int64_t> AtomicInt64; /// int64_t atomic integer.
  333. typedef AtomicInt<uint64_t> AtomicUint64; /// uint64_t atomic integer.
  334. #if !defined(EA_PLATFORM_WORD_SIZE) || (EA_PLATFORM_WORD_SIZE == 4)
  335. typedef AtomicInt32 AtomicIWord;
  336. typedef AtomicUint32 AtomicUWord;
  337. #else
  338. typedef AtomicInt64 AtomicIWord;
  339. typedef AtomicUint64 AtomicUWord;
  340. #endif
  341. #if !defined(EA_PLATFORM_PTR_SIZE) || (EA_PLATFORM_PTR_SIZE == 4)
  342. typedef AtomicInt32 AtomicIntPtr;
  343. typedef AtomicUint32 AtomicUintPtr;
  344. #else
  345. typedef AtomicInt64 AtomicIntPtr;
  346. typedef AtomicUint64 AtomicUintPtr;
  347. #endif
  348. // VC++ yields spurious warnings about void* being cast to an integer type and vice-versa.
  349. // These warnings are baseless because we check for platform pointer size above.
  350. EA_DISABLE_VC_WARNING(4311 4312 4251)
  351. /// class AtomicPointer
  352. ///
  353. /// For simplicity of the current implementation, we simply have AtomicPointer map
  354. /// to AtomicInt32. This is reasonably safe because AtomicInt32 uses intptr_t
  355. /// as its ValueType and there are no foreseeble supported platforms in which
  356. /// intptr_t will not exist or be possible as a data type.
  357. ///
  358. class EATHREADLIB_API AtomicPointer : public AtomicIntPtr
  359. {
  360. public:
  361. typedef void* PointerValueType;
  362. AtomicPointer(void* p = NULL)
  363. : AtomicIntPtr(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p))) {}
  364. AtomicPointer& operator=(void* p)
  365. { AtomicIntPtr::operator=(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p))); return *this; }
  366. operator const void*() const // It's debateable whether this should be supported.
  367. { return (void*)AtomicIntPtr::GetValue(); }
  368. void* GetValue() const
  369. { return (void*)AtomicIntPtr::GetValue(); }
  370. void* GetValueRaw() const
  371. { return (void*)AtomicIntPtr::GetValueRaw(); }
  372. void* SetValue(void* p)
  373. { return (void*)AtomicIntPtr::SetValue(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p))); }
  374. bool SetValueConditional(void* p, void* pCondition)
  375. { return AtomicIntPtr::SetValueConditional(static_cast<ValueType>(reinterpret_cast<uintptr_t>(p)), static_cast<ValueType>(reinterpret_cast<uintptr_t>(pCondition))); }
  376. };
  377. EA_RESTORE_VC_WARNING()
  378. } // namespace Thread
  379. } // namespace EA
  380. #endif // EATHREAD_EATHREAD_ATOMIC_H