atomic_counter.hpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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_COUNTER_HPP_INCLUDED__
  25. #define __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__
  26. #include "stdint.hpp"
  27. #include "macros.hpp"
  28. #if defined ZMQ_FORCE_MUTEXES
  29. #define ZMQ_ATOMIC_COUNTER_MUTEX
  30. #elif (defined __cplusplus && __cplusplus >= 201103L) \
  31. || (defined _MSC_VER && _MSC_VER >= 1900)
  32. #define ZMQ_ATOMIC_COUNTER_CXX11
  33. #elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
  34. #define ZMQ_ATOMIC_COUNTER_INTRINSIC
  35. #elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
  36. #define ZMQ_ATOMIC_COUNTER_X86
  37. #elif defined __ARM_ARCH_7A__ && defined __GNUC__
  38. #define ZMQ_ATOMIC_COUNTER_ARM
  39. #elif defined ZMQ_HAVE_WINDOWS
  40. #define ZMQ_ATOMIC_COUNTER_WINDOWS
  41. #elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD \
  42. || defined ZMQ_HAVE_GNU)
  43. #define ZMQ_ATOMIC_COUNTER_ATOMIC_H
  44. #elif defined __tile__
  45. #define ZMQ_ATOMIC_COUNTER_TILE
  46. #else
  47. #define ZMQ_ATOMIC_COUNTER_MUTEX
  48. #endif
  49. #if defined ZMQ_ATOMIC_COUNTER_MUTEX
  50. #include "mutex.hpp"
  51. #elif defined ZMQ_ATOMIC_COUNTER_CXX11
  52. #include <atomic>
  53. #elif defined ZMQ_ATOMIC_COUNTER_WINDOWS
  54. #include "windows.hpp"
  55. #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
  56. #include <atomic.h>
  57. #elif defined ZMQ_ATOMIC_COUNTER_TILE
  58. #include <arch/atomic.h>
  59. #endif
  60. namespace zmq
  61. {
  62. // This class represents an integer that can be incremented/decremented
  63. // in atomic fashion.
  64. //
  65. // In zmq::shared_message_memory_allocator a buffer with an atomic_counter_t
  66. // at the start is allocated. If the class does not align to pointer size,
  67. // access to pointers in structures in the buffer will cause SIGBUS on
  68. // architectures that do not allow mis-aligned pointers (eg: SPARC).
  69. // Force the compiler to align to pointer size, which will cause the object
  70. // to grow from 4 bytes to 8 bytes on 64 bit architectures (when not using
  71. // mutexes).
  72. #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
  73. class __declspec(align (8)) atomic_counter_t
  74. #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE))
  75. class __declspec(align (4)) atomic_counter_t
  76. #else
  77. class atomic_counter_t
  78. #endif
  79. {
  80. public:
  81. typedef uint32_t integer_t;
  82. atomic_counter_t (integer_t value_ = 0) ZMQ_NOEXCEPT : _value (value_) {}
  83. // Set counter _value (not thread-safe).
  84. void set (integer_t value_) ZMQ_NOEXCEPT { _value = value_; }
  85. // Atomic addition. Returns the old _value.
  86. integer_t add (integer_t increment_) ZMQ_NOEXCEPT
  87. {
  88. integer_t old_value;
  89. #if defined ZMQ_ATOMIC_COUNTER_WINDOWS
  90. old_value = InterlockedExchangeAdd ((LONG *) &_value, increment_);
  91. #elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC
  92. old_value = __atomic_fetch_add (&_value, increment_, __ATOMIC_ACQ_REL);
  93. #elif defined ZMQ_ATOMIC_COUNTER_CXX11
  94. old_value = _value.fetch_add (increment_, std::memory_order_acq_rel);
  95. #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
  96. integer_t new_value = atomic_add_32_nv (&_value, increment_);
  97. old_value = new_value - increment_;
  98. #elif defined ZMQ_ATOMIC_COUNTER_TILE
  99. old_value = arch_atomic_add (&_value, increment_);
  100. #elif defined ZMQ_ATOMIC_COUNTER_X86
  101. __asm__ volatile("lock; xadd %0, %1 \n\t"
  102. : "=r"(old_value), "=m"(_value)
  103. : "0"(increment_), "m"(_value)
  104. : "cc", "memory");
  105. #elif defined ZMQ_ATOMIC_COUNTER_ARM
  106. integer_t flag, tmp;
  107. __asm__ volatile(" dmb sy\n\t"
  108. "1: ldrex %0, [%5]\n\t"
  109. " add %2, %0, %4\n\t"
  110. " strex %1, %2, [%5]\n\t"
  111. " teq %1, #0\n\t"
  112. " bne 1b\n\t"
  113. " dmb sy\n\t"
  114. : "=&r"(old_value), "=&r"(flag), "=&r"(tmp),
  115. "+Qo"(_value)
  116. : "Ir"(increment_), "r"(&_value)
  117. : "cc");
  118. #elif defined ZMQ_ATOMIC_COUNTER_MUTEX
  119. sync.lock ();
  120. old_value = _value;
  121. _value += increment_;
  122. sync.unlock ();
  123. #else
  124. #error atomic_counter is not implemented for this platform
  125. #endif
  126. return old_value;
  127. }
  128. // Atomic subtraction. Returns false if the counter drops to zero.
  129. bool sub (integer_t decrement_) ZMQ_NOEXCEPT
  130. {
  131. #if defined ZMQ_ATOMIC_COUNTER_WINDOWS
  132. LONG delta = -((LONG) decrement_);
  133. integer_t old = InterlockedExchangeAdd ((LONG *) &_value, delta);
  134. return old - decrement_ != 0;
  135. #elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC
  136. integer_t nv =
  137. __atomic_sub_fetch (&_value, decrement_, __ATOMIC_ACQ_REL);
  138. return nv != 0;
  139. #elif defined ZMQ_ATOMIC_COUNTER_CXX11
  140. const integer_t old =
  141. _value.fetch_sub (decrement_, std::memory_order_acq_rel);
  142. return old - decrement_ != 0;
  143. #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
  144. int32_t delta = -((int32_t) decrement_);
  145. integer_t nv = atomic_add_32_nv (&_value, delta);
  146. return nv != 0;
  147. #elif defined ZMQ_ATOMIC_COUNTER_TILE
  148. int32_t delta = -((int32_t) decrement_);
  149. integer_t nv = arch_atomic_add (&_value, delta);
  150. return nv != 0;
  151. #elif defined ZMQ_ATOMIC_COUNTER_X86
  152. integer_t oldval = -decrement_;
  153. volatile integer_t *val = &_value;
  154. __asm__ volatile("lock; xaddl %0,%1"
  155. : "=r"(oldval), "=m"(*val)
  156. : "0"(oldval), "m"(*val)
  157. : "cc", "memory");
  158. return oldval != decrement_;
  159. #elif defined ZMQ_ATOMIC_COUNTER_ARM
  160. integer_t old_value, flag, tmp;
  161. __asm__ volatile(" dmb sy\n\t"
  162. "1: ldrex %0, [%5]\n\t"
  163. " sub %2, %0, %4\n\t"
  164. " strex %1, %2, [%5]\n\t"
  165. " teq %1, #0\n\t"
  166. " bne 1b\n\t"
  167. " dmb sy\n\t"
  168. : "=&r"(old_value), "=&r"(flag), "=&r"(tmp),
  169. "+Qo"(_value)
  170. : "Ir"(decrement_), "r"(&_value)
  171. : "cc");
  172. return old_value - decrement_ != 0;
  173. #elif defined ZMQ_ATOMIC_COUNTER_MUTEX
  174. sync.lock ();
  175. _value -= decrement_;
  176. bool result = _value ? true : false;
  177. sync.unlock ();
  178. return result;
  179. #else
  180. #error atomic_counter is not implemented for this platform
  181. #endif
  182. }
  183. integer_t get () const ZMQ_NOEXCEPT { return _value; }
  184. private:
  185. #if defined ZMQ_ATOMIC_COUNTER_CXX11
  186. std::atomic<integer_t> _value;
  187. #else
  188. volatile integer_t _value;
  189. #endif
  190. #if defined ZMQ_ATOMIC_COUNTER_MUTEX
  191. mutex_t sync;
  192. #endif
  193. #if !defined ZMQ_ATOMIC_COUNTER_CXX11
  194. ZMQ_NON_COPYABLE_NOR_MOVABLE (atomic_counter_t)
  195. #endif
  196. #if defined(__GNUC__) || defined(__INTEL_COMPILER) \
  197. || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590) \
  198. || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590)
  199. } __attribute__ ((aligned (sizeof (void *))));
  200. #else
  201. };
  202. #endif
  203. }
  204. // Remove macros local to this file.
  205. #undef ZMQ_ATOMIC_COUNTER_MUTEX
  206. #undef ZMQ_ATOMIC_COUNTER_INTRINSIC
  207. #undef ZMQ_ATOMIC_COUNTER_CXX11
  208. #undef ZMQ_ATOMIC_COUNTER_X86
  209. #undef ZMQ_ATOMIC_COUNTER_ARM
  210. #undef ZMQ_ATOMIC_COUNTER_WINDOWS
  211. #undef ZMQ_ATOMIC_COUNTER_ATOMIC_H
  212. #undef ZMQ_ATOMIC_COUNTER_TILE
  213. #endif