atomic.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. //----------------------------------------------------------------------------
  2. // This is free and unencumbered software released into the public domain.
  3. //
  4. // Anyone is free to copy, modify, publish, use, compile, sell, or distribute
  5. // this software, either in source code form or as a compiled binary, for any
  6. // purpose, commercial or non-commercial, and by any means.
  7. //
  8. // In jurisdictions that recognize copyright laws, the author or authors of
  9. // this software dedicate any and all copyright interest in the software to the
  10. // public domain. We make this dedication for the benefit of the public at
  11. // large and to the detriment of our heirs and successors. We intend this
  12. // dedication to be an overt act of relinquishment in perpetuity of all present
  13. // and future rights to this software under copyright law.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  19. // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. // For more information, please refer to <http://unlicense.org/>
  23. //-----------------------------------------------------------------------------
  24. // SRT Project information:
  25. // This file was adopted from a Public Domain project from
  26. // https://github.com/mbitsnbites/atomic
  27. // Only namespaces were changed to adopt it for SRT project.
  28. #ifndef SRT_SYNC_ATOMIC_H_
  29. #define SRT_SYNC_ATOMIC_H_
  30. // Macro for disallowing copying of an object.
  31. #if __cplusplus >= 201103L
  32. #define ATOMIC_DISALLOW_COPY(T) \
  33. T(const T&) = delete; \
  34. T& operator=(const T&) = delete;
  35. #else
  36. #define ATOMIC_DISALLOW_COPY(T) \
  37. T(const T&); \
  38. T& operator=(const T&);
  39. #endif
  40. // A portable static assert.
  41. #if __cplusplus >= 201103L
  42. #define ATOMIC_STATIC_ASSERT(condition, message) \
  43. static_assert((condition), message)
  44. #else
  45. // Based on: http://stackoverflow.com/a/809465/5778708
  46. #define ATOMIC_STATIC_ASSERT(condition, message) \
  47. _impl_STATIC_ASSERT_LINE(condition, __LINE__)
  48. #define _impl_PASTE(a, b) a##b
  49. #ifdef __GNUC__
  50. #define _impl_UNUSED __attribute__((__unused__))
  51. #else
  52. #define _impl_UNUSED
  53. #endif
  54. #define _impl_STATIC_ASSERT_LINE(condition, line) \
  55. typedef char _impl_PASTE( \
  56. STATIC_ASSERT_failed_, \
  57. line)[(2 * static_cast<int>(!!(condition))) - 1] _impl_UNUSED
  58. #endif
  59. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  60. // NOTE: Defined at the top level.
  61. #elif __cplusplus >= 201103L
  62. // NOTE: Prefer to use the c++11 std::atomic.
  63. #define ATOMIC_USE_CPP11_ATOMIC
  64. #elif (defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5)) \
  65. || defined(__xlc__)
  66. // NOTE: Clang <6 does not support GCC __atomic_* intrinsics. I am unsure
  67. // about Clang6. Since Clang sets __GNUC__ and __GNUC_MINOR__ of this era
  68. // to <4.5, older Clang will catch the setting below to use the
  69. // POSIX Mutex Implementation.
  70. #define ATOMIC_USE_GCC_INTRINSICS
  71. #elif defined(__GNUC__) \
  72. && ( (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) )
  73. // NOTE: The __atomic_* family of intrisics were introduced in GCC-4.7.0.
  74. // NOTE: This follows #if defined(__clang__), because most if, not all,
  75. // versions of Clang define __GNUC__ and __GNUC_MINOR__ but often define
  76. // them to 4.4 or an even earlier version. Most of the newish versions
  77. // of Clang also support GCC Atomic Intrisics even if they set GCC version
  78. // macros to <4.7.
  79. #define ATOMIC_USE_GCC_INTRINSICS
  80. #elif defined(__GNUC__) && !defined(ATOMIC_USE_SRT_SYNC_MUTEX)
  81. // NOTE: GCC compiler built-ins for atomic operations are pure
  82. // compiler extensions prior to GCC-4.7 and were grouped into the
  83. // the __sync_* family of functions. GCC-4.7, both the c++11 and C11
  84. // standards had been finalized, and GCC updated their built-ins to
  85. // better reflect the new memory model and the new functions grouped
  86. // into the __atomic_* family. Also the memory models were defined
  87. // differently, than in pre 4.7.
  88. // TODO: PORT to the pre GCC-4.7 __sync_* intrinsics. In the meantime use
  89. // the POSIX Mutex Implementation.
  90. #define ATOMIC_USE_SRT_SYNC_MUTEX 1
  91. #elif defined(_MSC_VER)
  92. #define ATOMIC_USE_MSVC_INTRINSICS
  93. #include "atomic_msvc.h"
  94. #else
  95. #error Unsupported compiler / system.
  96. #endif
  97. // Include any necessary headers for the selected Atomic Implementation.
  98. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  99. #include "sync.h"
  100. #endif
  101. #if defined(ATOMIC_USE_CPP11_ATOMIC)
  102. #include <atomic>
  103. #endif
  104. namespace srt {
  105. namespace sync {
  106. template <typename T>
  107. class atomic {
  108. public:
  109. ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 ||
  110. sizeof(T) == 8,
  111. "Only types of size 1, 2, 4 or 8 are supported");
  112. atomic()
  113. : value_(static_cast<T>(0))
  114. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  115. , mutex_()
  116. #endif
  117. {
  118. // No-Op
  119. }
  120. explicit atomic(const T value)
  121. : value_(value)
  122. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  123. , mutex_()
  124. #endif
  125. {
  126. // No-Op
  127. }
  128. ~atomic()
  129. {
  130. // No-Op
  131. }
  132. /// @brief Performs an atomic increment operation (value + 1).
  133. /// @returns The new value of the atomic object.
  134. T operator++() {
  135. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  136. ScopedLock lg_(mutex_);
  137. const T t = ++value_;
  138. return t;
  139. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  140. return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST);
  141. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  142. return msvc::interlocked<T>::increment(&value_);
  143. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  144. return ++value_;
  145. #else
  146. #error "Implement Me."
  147. #endif
  148. }
  149. /// @brief Performs an atomic decrement operation (value - 1).
  150. /// @returns The new value of the atomic object.
  151. T operator--() {
  152. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  153. ScopedLock lg_(mutex_);
  154. const T t = --value_;
  155. return t;
  156. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  157. return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST);
  158. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  159. return msvc::interlocked<T>::decrement(&value_);
  160. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  161. return --value_;
  162. #else
  163. #error "Implement Me."
  164. #endif
  165. }
  166. /// @brief Performs an atomic compare-and-swap (CAS) operation.
  167. ///
  168. /// The value of the atomic object is only updated to the new value if the
  169. /// old value of the atomic object matches @c expected_val.
  170. ///
  171. /// @param expected_val The expected value of the atomic object.
  172. /// @param new_val The new value to write to the atomic object.
  173. /// @returns True if new_value was written to the atomic object.
  174. bool compare_exchange(const T expected_val, const T new_val) {
  175. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  176. ScopedLock lg_(mutex_);
  177. bool result = false;
  178. if (expected_val == value_)
  179. {
  180. value_ = new_val;
  181. result = true;
  182. }
  183. return result;
  184. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  185. T e = expected_val;
  186. return __atomic_compare_exchange_n(
  187. &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
  188. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  189. const T old_val =
  190. msvc::interlocked<T>::compare_exchange(&value_, new_val, expected_val);
  191. return (old_val == expected_val);
  192. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  193. T e = expected_val;
  194. return value_.compare_exchange_weak(e, new_val);
  195. #else
  196. #error "Implement Me."
  197. #endif
  198. }
  199. /// @brief Performs an atomic set operation.
  200. ///
  201. /// The value of the atomic object is unconditionally updated to the new
  202. /// value.
  203. ///
  204. /// @param new_val The new value to write to the atomic object.
  205. void store(const T new_val) {
  206. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  207. ScopedLock lg_(mutex_);
  208. value_ = new_val;
  209. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  210. __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST);
  211. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  212. (void)msvc::interlocked<T>::exchange(&value_, new_val);
  213. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  214. value_.store(new_val);
  215. #else
  216. #error "Implement Me."
  217. #endif
  218. }
  219. /// @returns the current value of the atomic object.
  220. /// @note Be careful about how this is used, since any operations on the
  221. /// returned value are inherently non-atomic.
  222. T load() const {
  223. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  224. ScopedLock lg_(mutex_);
  225. const T t = value_;
  226. return t;
  227. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  228. return __atomic_load_n(&value_, __ATOMIC_SEQ_CST);
  229. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  230. // TODO(m): Is there a better solution for MSVC?
  231. return value_;
  232. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  233. return value_;
  234. #else
  235. #error "Implement Me."
  236. #endif
  237. }
  238. /// @brief Performs an atomic exchange operation.
  239. ///
  240. /// The value of the atomic object is unconditionally updated to the new
  241. /// value, and the old value is returned.
  242. ///
  243. /// @param new_val The new value to write to the atomic object.
  244. /// @returns the old value.
  245. T exchange(const T new_val) {
  246. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  247. ScopedLock lg_(mutex_);
  248. const T t = value_;
  249. value_ = new_val;
  250. return t;
  251. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  252. return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST);
  253. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  254. return msvc::interlocked<T>::exchange(&value_, new_val);
  255. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  256. return value_.exchange(new_val);
  257. #else
  258. #error "Implement Me."
  259. #endif
  260. }
  261. T operator=(const T new_value) {
  262. store(new_value);
  263. return new_value;
  264. }
  265. operator T() const {
  266. return load();
  267. }
  268. private:
  269. #if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
  270. T value_;
  271. mutable Mutex mutex_;
  272. #elif defined(ATOMIC_USE_GCC_INTRINSICS)
  273. volatile T value_;
  274. #elif defined(ATOMIC_USE_MSVC_INTRINSICS)
  275. volatile T value_;
  276. #elif defined(ATOMIC_USE_CPP11_ATOMIC)
  277. std::atomic<T> value_;
  278. #else
  279. #error "Implement Me. (value_ type)"
  280. #endif
  281. ATOMIC_DISALLOW_COPY(atomic)
  282. };
  283. } // namespace sync
  284. } // namespace srt
  285. // Undef temporary defines.
  286. #undef ATOMIC_USE_GCC_INTRINSICS
  287. #undef ATOMIC_USE_MSVC_INTRINSICS
  288. #undef ATOMIC_USE_CPP11_ATOMIC
  289. #endif // ATOMIC_ATOMIC_H_