atomic_ptr.hpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
  3. This file is part of libzmq, the ZeroMQ core engine in C++.
  4. libzmq is free software; you can redistribute it and/or modify it under
  5. the terms of the GNU Lesser General Public License (LGPL) as published
  6. by the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. As a special exception, the Contributors give you permission to link
  9. this library with independent modules to produce an executable,
  10. regardless of the license terms of these independent modules, and to
  11. copy and distribute the resulting executable under terms of your choice,
  12. provided that you also meet, for each linked independent module, the
  13. terms and conditions of the license of that module. An independent
  14. module is a module which is not derived from or based on this library.
  15. If you modify this library, you must extend this exception to your
  16. version of the library.
  17. libzmq is distributed in the hope that it will be useful, but WITHOUT
  18. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  19. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  20. License for more details.
  21. You should have received a copy of the GNU Lesser General Public License
  22. along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. #ifndef __ZMQ_ATOMIC_PTR_HPP_INCLUDED__
  25. #define __ZMQ_ATOMIC_PTR_HPP_INCLUDED__
  26. #include "macros.hpp"
  27. #if defined ZMQ_FORCE_MUTEXES
  28. #define ZMQ_ATOMIC_PTR_MUTEX
  29. #elif (defined __cplusplus && __cplusplus >= 201103L) \
  30. || (defined _MSC_VER && _MSC_VER >= 1900)
  31. #define ZMQ_ATOMIC_PTR_CXX11
  32. #elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
  33. #define ZMQ_ATOMIC_PTR_INTRINSIC
  34. #elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
  35. #define ZMQ_ATOMIC_PTR_X86
  36. #elif defined __ARM_ARCH_7A__ && defined __GNUC__
  37. #define ZMQ_ATOMIC_PTR_ARM
  38. #elif defined __tile__
  39. #define ZMQ_ATOMIC_PTR_TILE
  40. #elif defined ZMQ_HAVE_WINDOWS
  41. #define ZMQ_ATOMIC_PTR_WINDOWS
  42. #elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD \
  43. || defined ZMQ_HAVE_GNU)
  44. #define ZMQ_ATOMIC_PTR_ATOMIC_H
  45. #else
  46. #define ZMQ_ATOMIC_PTR_MUTEX
  47. #endif
  48. #if defined ZMQ_ATOMIC_PTR_MUTEX
  49. #include "mutex.hpp"
  50. #elif defined ZMQ_ATOMIC_PTR_CXX11
  51. #include <atomic>
  52. #elif defined ZMQ_ATOMIC_PTR_WINDOWS
  53. #include "windows.hpp"
  54. #elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
  55. #include <atomic.h>
  56. #elif defined ZMQ_ATOMIC_PTR_TILE
  57. #include <arch/atomic.h>
  58. #endif
  59. namespace zmq
  60. {
  61. #if !defined ZMQ_ATOMIC_PTR_CXX11
  62. inline void *atomic_xchg_ptr (void **ptr_,
  63. void *const val_
  64. #if defined ZMQ_ATOMIC_PTR_MUTEX
  65. ,
  66. mutex_t &_sync
  67. #endif
  68. ) ZMQ_NOEXCEPT
  69. {
  70. #if defined ZMQ_ATOMIC_PTR_WINDOWS
  71. return InterlockedExchangePointer ((PVOID *) ptr_, val_);
  72. #elif defined ZMQ_ATOMIC_PTR_INTRINSIC
  73. return __atomic_exchange_n (ptr_, val_, __ATOMIC_ACQ_REL);
  74. #elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
  75. return atomic_swap_ptr (ptr_, val_);
  76. #elif defined ZMQ_ATOMIC_PTR_TILE
  77. return arch_atomic_exchange (ptr_, val_);
  78. #elif defined ZMQ_ATOMIC_PTR_X86
  79. void *old;
  80. __asm__ volatile("lock; xchg %0, %2"
  81. : "=r"(old), "=m"(*ptr_)
  82. : "m"(*ptr_), "0"(val_));
  83. return old;
  84. #elif defined ZMQ_ATOMIC_PTR_ARM
  85. void *old;
  86. unsigned int flag;
  87. __asm__ volatile(" dmb sy\n\t"
  88. "1: ldrex %1, [%3]\n\t"
  89. " strex %0, %4, [%3]\n\t"
  90. " teq %0, #0\n\t"
  91. " bne 1b\n\t"
  92. " dmb sy\n\t"
  93. : "=&r"(flag), "=&r"(old), "+Qo"(*ptr_)
  94. : "r"(ptr_), "r"(val_)
  95. : "cc");
  96. return old;
  97. #elif defined ZMQ_ATOMIC_PTR_MUTEX
  98. _sync.lock ();
  99. void *old = *ptr_;
  100. *ptr_ = val_;
  101. _sync.unlock ();
  102. return old;
  103. #else
  104. #error atomic_ptr is not implemented for this platform
  105. #endif
  106. }
  107. inline void *atomic_cas (void *volatile *ptr_,
  108. void *cmp_,
  109. void *val_
  110. #if defined ZMQ_ATOMIC_PTR_MUTEX
  111. ,
  112. mutex_t &_sync
  113. #endif
  114. ) ZMQ_NOEXCEPT
  115. {
  116. #if defined ZMQ_ATOMIC_PTR_WINDOWS
  117. return InterlockedCompareExchangePointer ((volatile PVOID *) ptr_, val_,
  118. cmp_);
  119. #elif defined ZMQ_ATOMIC_PTR_INTRINSIC
  120. void *old = cmp_;
  121. __atomic_compare_exchange_n (ptr_, &old, val_, false, __ATOMIC_RELEASE,
  122. __ATOMIC_ACQUIRE);
  123. return old;
  124. #elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
  125. return atomic_cas_ptr (ptr_, cmp_, val_);
  126. #elif defined ZMQ_ATOMIC_PTR_TILE
  127. return arch_atomic_val_compare_and_exchange (ptr_, cmp_, val_);
  128. #elif defined ZMQ_ATOMIC_PTR_X86
  129. void *old;
  130. __asm__ volatile("lock; cmpxchg %2, %3"
  131. : "=a"(old), "=m"(*ptr_)
  132. : "r"(val_), "m"(*ptr_), "0"(cmp_)
  133. : "cc");
  134. return old;
  135. #elif defined ZMQ_ATOMIC_PTR_ARM
  136. void *old;
  137. unsigned int flag;
  138. __asm__ volatile(" dmb sy\n\t"
  139. "1: ldrex %1, [%3]\n\t"
  140. " mov %0, #0\n\t"
  141. " teq %1, %4\n\t"
  142. " it eq\n\t"
  143. " strexeq %0, %5, [%3]\n\t"
  144. " teq %0, #0\n\t"
  145. " bne 1b\n\t"
  146. " dmb sy\n\t"
  147. : "=&r"(flag), "=&r"(old), "+Qo"(*ptr_)
  148. : "r"(ptr_), "r"(cmp_), "r"(val_)
  149. : "cc");
  150. return old;
  151. #elif defined ZMQ_ATOMIC_PTR_MUTEX
  152. _sync.lock ();
  153. void *old = *ptr_;
  154. if (*ptr_ == cmp_)
  155. *ptr_ = val_;
  156. _sync.unlock ();
  157. return old;
  158. #else
  159. #error atomic_ptr is not implemented for this platform
  160. #endif
  161. }
  162. #endif
  163. // This class encapsulates several atomic operations on pointers.
  164. template <typename T> class atomic_ptr_t
  165. {
  166. public:
  167. // Initialise atomic pointer
  168. atomic_ptr_t () ZMQ_NOEXCEPT { _ptr = NULL; }
  169. // Set value of atomic pointer in a non-threadsafe way
  170. // Use this function only when you are sure that at most one
  171. // thread is accessing the pointer at the moment.
  172. void set (T *ptr_) ZMQ_NOEXCEPT { _ptr = ptr_; }
  173. // Perform atomic 'exchange pointers' operation. Pointer is set
  174. // to the 'val_' value. Old value is returned.
  175. T *xchg (T *val_) ZMQ_NOEXCEPT
  176. {
  177. #if defined ZMQ_ATOMIC_PTR_CXX11
  178. return _ptr.exchange (val_, std::memory_order_acq_rel);
  179. #else
  180. return (T *) atomic_xchg_ptr ((void **) &_ptr, val_
  181. #if defined ZMQ_ATOMIC_PTR_MUTEX
  182. ,
  183. _sync
  184. #endif
  185. );
  186. #endif
  187. }
  188. // Perform atomic 'compare and swap' operation on the pointer.
  189. // The pointer is compared to 'cmp' argument and if they are
  190. // equal, its value is set to 'val_'. Old value of the pointer
  191. // is returned.
  192. T *cas (T *cmp_, T *val_) ZMQ_NOEXCEPT
  193. {
  194. #if defined ZMQ_ATOMIC_PTR_CXX11
  195. _ptr.compare_exchange_strong (cmp_, val_, std::memory_order_acq_rel);
  196. return cmp_;
  197. #else
  198. return (T *) atomic_cas ((void **) &_ptr, cmp_, val_
  199. #if defined ZMQ_ATOMIC_PTR_MUTEX
  200. ,
  201. _sync
  202. #endif
  203. );
  204. #endif
  205. }
  206. private:
  207. #if defined ZMQ_ATOMIC_PTR_CXX11
  208. std::atomic<T *> _ptr;
  209. #else
  210. volatile T *_ptr;
  211. #endif
  212. #if defined ZMQ_ATOMIC_PTR_MUTEX
  213. mutex_t _sync;
  214. #endif
  215. #if !defined ZMQ_ATOMIC_PTR_CXX11
  216. ZMQ_NON_COPYABLE_NOR_MOVABLE (atomic_ptr_t)
  217. #endif
  218. };
  219. struct atomic_value_t
  220. {
  221. atomic_value_t (const int value_) ZMQ_NOEXCEPT : _value (value_) {}
  222. atomic_value_t (const atomic_value_t &src_) ZMQ_NOEXCEPT
  223. : _value (src_.load ())
  224. {
  225. }
  226. void store (const int value_) ZMQ_NOEXCEPT
  227. {
  228. #if defined ZMQ_ATOMIC_PTR_CXX11
  229. _value.store (value_, std::memory_order_release);
  230. #else
  231. atomic_xchg_ptr ((void **) &_value, (void *) (ptrdiff_t) value_
  232. #if defined ZMQ_ATOMIC_PTR_MUTEX
  233. ,
  234. _sync
  235. #endif
  236. );
  237. #endif
  238. }
  239. int load () const ZMQ_NOEXCEPT
  240. {
  241. #if defined ZMQ_ATOMIC_PTR_CXX11
  242. return _value.load (std::memory_order_acquire);
  243. #else
  244. return (int) (ptrdiff_t) atomic_cas ((void **) &_value, 0, 0
  245. #if defined ZMQ_ATOMIC_PTR_MUTEX
  246. ,
  247. #if defined __SUNPRO_CC
  248. const_cast<mutex_t &> (_sync)
  249. #else
  250. _sync
  251. #endif
  252. #endif
  253. );
  254. #endif
  255. }
  256. private:
  257. #if defined ZMQ_ATOMIC_PTR_CXX11
  258. std::atomic<int> _value;
  259. #else
  260. volatile ptrdiff_t _value;
  261. #endif
  262. #if defined ZMQ_ATOMIC_PTR_MUTEX
  263. mutable mutex_t _sync;
  264. #endif
  265. private:
  266. atomic_value_t &operator= (const atomic_value_t &src_);
  267. };
  268. }
  269. // Remove macros local to this file.
  270. #undef ZMQ_ATOMIC_PTR_MUTEX
  271. #undef ZMQ_ATOMIC_PTR_INTRINSIC
  272. #undef ZMQ_ATOMIC_PTR_CXX11
  273. #undef ZMQ_ATOMIC_PTR_X86
  274. #undef ZMQ_ATOMIC_PTR_ARM
  275. #undef ZMQ_ATOMIC_PTR_TILE
  276. #undef ZMQ_ATOMIC_PTR_WINDOWS
  277. #undef ZMQ_ATOMIC_PTR_ATOMIC_H
  278. #endif