Line data Source code
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 "platform.hpp"
35 :
36 : #if defined ZMQ_FORCE_MUTEXES
37 : #define ZMQ_ATOMIC_COUNTER_MUTEX
38 : #elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
39 : #define ZMQ_ATOMIC_COUNTER_INTRINSIC
40 : #elif (defined ZMQ_CXX11 && defined __cplusplus && __cplusplus >= 201103L)
41 : #define ZMQ_ATOMIC_COUNTER_CXX11
42 : #elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
43 : #define ZMQ_ATOMIC_COUNTER_X86
44 : #elif defined __ARM_ARCH_7A__ && defined __GNUC__
45 : #define ZMQ_ATOMIC_COUNTER_ARM
46 : #elif defined ZMQ_HAVE_WINDOWS
47 : #define ZMQ_ATOMIC_COUNTER_WINDOWS
48 : #elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD || defined ZMQ_HAVE_GNU)
49 : #define ZMQ_ATOMIC_COUNTER_ATOMIC_H
50 : #elif defined __tile__
51 : #define ZMQ_ATOMIC_COUNTER_TILE
52 : #else
53 : #define ZMQ_ATOMIC_COUNTER_MUTEX
54 : #endif
55 :
56 : #if defined ZMQ_ATOMIC_COUNTER_MUTEX
57 : #include "mutex.hpp"
58 : #elif defined ZMQ_ATOMIC_COUNTER_CXX11
59 : #include <atomic>
60 : #elif defined ZMQ_ATOMIC_COUNTER_WINDOWS
61 : #include "windows.hpp"
62 : #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
63 : #include <atomic.h>
64 : #elif defined ZMQ_ATOMIC_COUNTER_TILE
65 : #include <arch/atomic.h>
66 : #endif
67 :
68 : namespace zmq
69 : {
70 :
71 : // This class represents an integer that can be incremented/decremented
72 : // in atomic fashion.
73 :
74 : class atomic_counter_t
75 : {
76 : public:
77 :
78 : typedef uint32_t integer_t;
79 :
80 : inline atomic_counter_t (integer_t value_ = 0) :
81 34722 : value (value_)
82 : {
83 : }
84 :
85 243 : inline ~atomic_counter_t ()
86 : {
87 243 : }
88 :
89 : // Set counter value (not thread-safe).
90 : inline void set (integer_t value_)
91 : {
92 9059 : value = value_;
93 : }
94 :
95 : // Atomic addition. Returns the old value.
96 : inline integer_t add (integer_t increment_)
97 : {
98 : integer_t old_value;
99 :
100 : #if defined ZMQ_ATOMIC_COUNTER_WINDOWS
101 : old_value = InterlockedExchangeAdd ((LONG*) &value, increment_);
102 : #elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC
103 705349 : old_value = __atomic_fetch_add(&value, increment_, __ATOMIC_ACQ_REL);
104 : #elif defined ZMQ_ATOMIC_COUNTER_CXX11
105 : old_value = value.fetch_add(increment_, std::memory_order_acq_rel);
106 : #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
107 : integer_t new_value = atomic_add_32_nv (&value, increment_);
108 : old_value = new_value - increment_;
109 : #elif defined ZMQ_ATOMIC_COUNTER_TILE
110 : old_value = arch_atomic_add (&value, increment_);
111 : #elif defined ZMQ_ATOMIC_COUNTER_X86
112 : __asm__ volatile (
113 : "lock; xadd %0, %1 \n\t"
114 : : "=r" (old_value), "=m" (value)
115 : : "0" (increment_), "m" (value)
116 : : "cc", "memory");
117 : #elif defined ZMQ_ATOMIC_COUNTER_ARM
118 : integer_t flag, tmp;
119 : __asm__ volatile (
120 : " dmb sy\n\t"
121 : "1: ldrex %0, [%5]\n\t"
122 : " add %2, %0, %4\n\t"
123 : " strex %1, %2, [%5]\n\t"
124 : " teq %1, #0\n\t"
125 : " bne 1b\n\t"
126 : " dmb sy\n\t"
127 : : "=&r"(old_value), "=&r"(flag), "=&r"(tmp), "+Qo"(value)
128 : : "Ir"(increment_), "r"(&value)
129 : : "cc");
130 : #elif defined ZMQ_ATOMIC_COUNTER_MUTEX
131 : sync.lock ();
132 : old_value = value;
133 : value += increment_;
134 : sync.unlock ();
135 : #else
136 : #error atomic_counter is not implemented for this platform
137 : #endif
138 : return old_value;
139 : }
140 :
141 : // Atomic subtraction. Returns false if the counter drops to zero.
142 : inline bool sub (integer_t decrement)
143 : {
144 : #if defined ZMQ_ATOMIC_COUNTER_WINDOWS
145 : LONG delta = - ((LONG) decrement);
146 : integer_t old = InterlockedExchangeAdd ((LONG*) &value, delta);
147 : return old - decrement != 0;
148 : #elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC
149 677193 : integer_t nv = __atomic_sub_fetch(&value, decrement, __ATOMIC_ACQ_REL);
150 : return nv != 0;
151 : #elif defined ZMQ_ATOMIC_COUNTER_CXX11
152 : integer_t old = value.fetch_sub(decrement, std::memory_order_acq_rel);
153 : return old - decrement != 0;
154 : #elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
155 : int32_t delta = - ((int32_t) decrement);
156 : integer_t nv = atomic_add_32_nv (&value, delta);
157 : return nv != 0;
158 : #elif defined ZMQ_ATOMIC_COUNTER_TILE
159 : int32_t delta = - ((int32_t) decrement);
160 : integer_t nv = arch_atomic_add (&value, delta);
161 : return nv != 0;
162 : #elif defined ZMQ_ATOMIC_COUNTER_X86
163 : integer_t oldval = -decrement;
164 : volatile integer_t *val = &value;
165 : __asm__ volatile ("lock; xaddl %0,%1"
166 : : "=r" (oldval), "=m" (*val)
167 : : "0" (oldval), "m" (*val)
168 : : "cc", "memory");
169 : return oldval != decrement;
170 : #elif defined ZMQ_ATOMIC_COUNTER_ARM
171 : integer_t old_value, flag, tmp;
172 : __asm__ volatile (
173 : " dmb sy\n\t"
174 : "1: ldrex %0, [%5]\n\t"
175 : " sub %2, %0, %4\n\t"
176 : " strex %1, %2, [%5]\n\t"
177 : " teq %1, #0\n\t"
178 : " bne 1b\n\t"
179 : " dmb sy\n\t"
180 : : "=&r"(old_value), "=&r"(flag), "=&r"(tmp), "+Qo"(value)
181 : : "Ir"(decrement), "r"(&value)
182 : : "cc");
183 : return old_value - decrement != 0;
184 : #elif defined ZMQ_ATOMIC_COUNTER_MUTEX
185 : sync.lock ();
186 : value -= decrement;
187 : bool result = value ? true : false;
188 : sync.unlock ();
189 : return result;
190 : #else
191 : #error atomic_counter is not implemented for this platform
192 : #endif
193 : }
194 :
195 : inline integer_t get () const
196 : {
197 111986 : return value;
198 : }
199 :
200 : private:
201 :
202 : #if defined ZMQ_ATOMIC_COUNTER_CXX11
203 : std::atomic<integer_t> value;
204 : #else
205 : volatile integer_t value;
206 : #endif
207 :
208 : #if defined ZMQ_ATOMIC_COUNTER_MUTEX
209 : mutex_t sync;
210 : #endif
211 :
212 : #if ! defined ZMQ_ATOMIC_COUNTER_CXX11
213 : atomic_counter_t (const atomic_counter_t&);
214 : const atomic_counter_t& operator = (const atomic_counter_t&);
215 : #endif
216 : };
217 :
218 : }
219 :
220 : // Remove macros local to this file.
221 : #undef ZMQ_ATOMIC_COUNTER_MUTEX
222 : #undef ZMQ_ATOMIC_COUNTER_INTRINSIC
223 : #undef ZMQ_ATOMIC_COUNTER_CXX11
224 : #undef ZMQ_ATOMIC_COUNTER_X86
225 : #undef ZMQ_ATOMIC_COUNTER_ARM
226 : #undef ZMQ_ATOMIC_COUNTER_WINDOWS
227 : #undef ZMQ_ATOMIC_COUNTER_ATOMIC_H
228 : #undef ZMQ_ATOMIC_COUNTER_TILE
229 :
230 : #endif
|