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
70namespace 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))
84class __declspec(align (8)) atomic_counter_t
85#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE))
86class __declspec(align (4)) atomic_counter_t
87#else
88class 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