1 | /* |
2 | Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file |
3 | |
4 | This file is part of libzmq, the ZeroMQ core engine in C++. |
5 | |
6 | libzmq is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU Lesser General Public License (LGPL) as published |
8 | by the Free Software Foundation; either version 3 of the License, or |
9 | (at your option) any later version. |
10 | |
11 | As a special exception, the Contributors give you permission to link |
12 | this library with independent modules to produce an executable, |
13 | regardless of the license terms of these independent modules, and to |
14 | copy and distribute the resulting executable under terms of your choice, |
15 | provided that you also meet, for each linked independent module, the |
16 | terms and conditions of the license of that module. An independent |
17 | module is a module which is not derived from or based on this library. |
18 | If you modify this library, you must extend this exception to your |
19 | version of the library. |
20 | |
21 | libzmq is distributed in the hope that it will be useful, but WITHOUT |
22 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
23 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
24 | License for more details. |
25 | |
26 | You should have received a copy of the GNU Lesser General Public License |
27 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
28 | */ |
29 | |
30 | #ifndef __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__ |
31 | #define __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__ |
32 | |
33 | #include "stdint.hpp" |
34 | #include "macros.hpp" |
35 | |
36 | #if defined ZMQ_FORCE_MUTEXES |
37 | #define ZMQ_ATOMIC_COUNTER_MUTEX |
38 | #elif (defined __cplusplus && __cplusplus >= 201103L) \ |
39 | || (defined _MSC_VER && _MSC_VER >= 1900) |
40 | #define ZMQ_ATOMIC_COUNTER_CXX11 |
41 | #elif defined ZMQ_HAVE_ATOMIC_INTRINSICS |
42 | #define ZMQ_ATOMIC_COUNTER_INTRINSIC |
43 | #elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ |
44 | #define ZMQ_ATOMIC_COUNTER_X86 |
45 | #elif defined __ARM_ARCH_7A__ && defined __GNUC__ |
46 | #define ZMQ_ATOMIC_COUNTER_ARM |
47 | #elif defined ZMQ_HAVE_WINDOWS |
48 | #define ZMQ_ATOMIC_COUNTER_WINDOWS |
49 | #elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD \ |
50 | || defined ZMQ_HAVE_GNU) |
51 | #define ZMQ_ATOMIC_COUNTER_ATOMIC_H |
52 | #elif defined __tile__ |
53 | #define ZMQ_ATOMIC_COUNTER_TILE |
54 | #else |
55 | #define ZMQ_ATOMIC_COUNTER_MUTEX |
56 | #endif |
57 | |
58 | #if defined ZMQ_ATOMIC_COUNTER_MUTEX |
59 | #include "mutex.hpp" |
60 | #elif defined ZMQ_ATOMIC_COUNTER_CXX11 |
61 | #include <atomic> |
62 | #elif defined ZMQ_ATOMIC_COUNTER_WINDOWS |
63 | #include "windows.hpp" |
64 | #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H |
65 | #include <atomic.h> |
66 | #elif defined ZMQ_ATOMIC_COUNTER_TILE |
67 | #include <arch/atomic.h> |
68 | #endif |
69 | |
70 | namespace zmq |
71 | { |
72 | // This class represents an integer that can be incremented/decremented |
73 | // in atomic fashion. |
74 | // |
75 | // In zmq::shared_message_memory_allocator a buffer with an atomic_counter_t |
76 | // at the start is allocated. If the class does not align to pointer size, |
77 | // access to pointers in structures in the buffer will cause SIGBUS on |
78 | // architectures that do not allow mis-aligned pointers (eg: SPARC). |
79 | // Force the compiler to align to pointer size, which will cause the object |
80 | // to grow from 4 bytes to 8 bytes on 64 bit architectures (when not using |
81 | // mutexes). |
82 | |
83 | #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) |
84 | class __declspec(align (8)) atomic_counter_t |
85 | #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE)) |
86 | class __declspec(align (4)) atomic_counter_t |
87 | #else |
88 | class atomic_counter_t |
89 | #endif |
90 | { |
91 | public: |
92 | typedef uint32_t integer_t; |
93 | |
94 | inline atomic_counter_t (integer_t value_ = 0) ZMQ_NOEXCEPT |
95 | : _value (value_) |
96 | { |
97 | } |
98 | |
99 | // Set counter _value (not thread-safe). |
100 | inline void set (integer_t value_) ZMQ_NOEXCEPT { _value = value_; } |
101 | |
102 | // Atomic addition. Returns the old _value. |
103 | inline integer_t add (integer_t increment_) ZMQ_NOEXCEPT |
104 | { |
105 | integer_t old_value; |
106 | |
107 | #if defined ZMQ_ATOMIC_COUNTER_WINDOWS |
108 | old_value = InterlockedExchangeAdd ((LONG *) &_value, increment_); |
109 | #elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC |
110 | old_value = __atomic_fetch_add (&_value, increment_, __ATOMIC_ACQ_REL); |
111 | #elif defined ZMQ_ATOMIC_COUNTER_CXX11 |
112 | old_value = _value.fetch_add (increment_, std::memory_order_acq_rel); |
113 | #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H |
114 | integer_t new_value = atomic_add_32_nv (&_value, increment_); |
115 | old_value = new_value - increment_; |
116 | #elif defined ZMQ_ATOMIC_COUNTER_TILE |
117 | old_value = arch_atomic_add (&_value, increment_); |
118 | #elif defined ZMQ_ATOMIC_COUNTER_X86 |
119 | __asm__ volatile("lock; xadd %0, %1 \n\t" |
120 | : "=r" (old_value), "=m" (_value) |
121 | : "0" (increment_), "m" (_value) |
122 | : "cc" , "memory" ); |
123 | #elif defined ZMQ_ATOMIC_COUNTER_ARM |
124 | integer_t flag, tmp; |
125 | __asm__ volatile(" dmb sy\n\t" |
126 | "1: ldrex %0, [%5]\n\t" |
127 | " add %2, %0, %4\n\t" |
128 | " strex %1, %2, [%5]\n\t" |
129 | " teq %1, #0\n\t" |
130 | " bne 1b\n\t" |
131 | " dmb sy\n\t" |
132 | : "=&r" (old_value), "=&r" (flag), "=&r" (tmp), |
133 | "+Qo" (_value) |
134 | : "Ir" (increment_), "r" (&_value) |
135 | : "cc" ); |
136 | #elif defined ZMQ_ATOMIC_COUNTER_MUTEX |
137 | sync.lock (); |
138 | old_value = _value; |
139 | _value += increment_; |
140 | sync.unlock (); |
141 | #else |
142 | #error atomic_counter is not implemented for this platform |
143 | #endif |
144 | return old_value; |
145 | } |
146 | |
147 | // Atomic subtraction. Returns false if the counter drops to zero. |
148 | inline bool sub (integer_t decrement_) ZMQ_NOEXCEPT |
149 | { |
150 | #if defined ZMQ_ATOMIC_COUNTER_WINDOWS |
151 | LONG delta = -((LONG) decrement_); |
152 | integer_t old = InterlockedExchangeAdd ((LONG *) &_value, delta); |
153 | return old - decrement_ != 0; |
154 | #elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC |
155 | integer_t nv = |
156 | __atomic_sub_fetch (&_value, decrement_, __ATOMIC_ACQ_REL); |
157 | return nv != 0; |
158 | #elif defined ZMQ_ATOMIC_COUNTER_CXX11 |
159 | integer_t old = |
160 | _value.fetch_sub (decrement_, std::memory_order_acq_rel); |
161 | return old - decrement_ != 0; |
162 | #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H |
163 | int32_t delta = -((int32_t) decrement_); |
164 | integer_t nv = atomic_add_32_nv (&_value, delta); |
165 | return nv != 0; |
166 | #elif defined ZMQ_ATOMIC_COUNTER_TILE |
167 | int32_t delta = -((int32_t) decrement_); |
168 | integer_t nv = arch_atomic_add (&_value, delta); |
169 | return nv != 0; |
170 | #elif defined ZMQ_ATOMIC_COUNTER_X86 |
171 | integer_t oldval = -decrement_; |
172 | volatile integer_t *val = &_value; |
173 | __asm__ volatile("lock; xaddl %0,%1" |
174 | : "=r" (oldval), "=m" (*val) |
175 | : "0" (oldval), "m" (*val) |
176 | : "cc" , "memory" ); |
177 | return oldval != decrement_; |
178 | #elif defined ZMQ_ATOMIC_COUNTER_ARM |
179 | integer_t old_value, flag, tmp; |
180 | __asm__ volatile(" dmb sy\n\t" |
181 | "1: ldrex %0, [%5]\n\t" |
182 | " sub %2, %0, %4\n\t" |
183 | " strex %1, %2, [%5]\n\t" |
184 | " teq %1, #0\n\t" |
185 | " bne 1b\n\t" |
186 | " dmb sy\n\t" |
187 | : "=&r" (old_value), "=&r" (flag), "=&r" (tmp), |
188 | "+Qo" (_value) |
189 | : "Ir" (decrement_), "r" (&_value) |
190 | : "cc" ); |
191 | return old_value - decrement_ != 0; |
192 | #elif defined ZMQ_ATOMIC_COUNTER_MUTEX |
193 | sync.lock (); |
194 | _value -= decrement_; |
195 | bool result = _value ? true : false; |
196 | sync.unlock (); |
197 | return result; |
198 | #else |
199 | #error atomic_counter is not implemented for this platform |
200 | #endif |
201 | } |
202 | |
203 | inline integer_t get () const ZMQ_NOEXCEPT { return _value; } |
204 | |
205 | private: |
206 | #if defined ZMQ_ATOMIC_COUNTER_CXX11 |
207 | std::atomic<integer_t> _value; |
208 | #else |
209 | volatile integer_t _value; |
210 | #endif |
211 | |
212 | #if defined ZMQ_ATOMIC_COUNTER_MUTEX |
213 | mutex_t sync; |
214 | #endif |
215 | |
216 | #if !defined ZMQ_ATOMIC_COUNTER_CXX11 |
217 | ZMQ_NON_COPYABLE_NOR_MOVABLE (atomic_counter_t) |
218 | #endif |
219 | #if defined(__GNUC__) || defined(__INTEL_COMPILER) \ |
220 | || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590) \ |
221 | || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590) |
222 | } __attribute__ ((aligned (sizeof (void *)))); |
223 | #else |
224 | }; |
225 | #endif |
226 | } |
227 | |
228 | // Remove macros local to this file. |
229 | #undef ZMQ_ATOMIC_COUNTER_MUTEX |
230 | #undef ZMQ_ATOMIC_COUNTER_INTRINSIC |
231 | #undef ZMQ_ATOMIC_COUNTER_CXX11 |
232 | #undef ZMQ_ATOMIC_COUNTER_X86 |
233 | #undef ZMQ_ATOMIC_COUNTER_ARM |
234 | #undef ZMQ_ATOMIC_COUNTER_WINDOWS |
235 | #undef ZMQ_ATOMIC_COUNTER_ATOMIC_H |
236 | #undef ZMQ_ATOMIC_COUNTER_TILE |
237 | |
238 | #endif |
239 | |