libzmq  master
ZeroMQ C++ Core Engine (LIBZMQ)
atomic_counter.hpp
Go to the documentation of this file.
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 
75  {
76  public:
77 
78  typedef uint32_t integer_t;
79 
80  inline atomic_counter_t (integer_t value_ = 0) :
81  value (value_)
82  {
83  }
84 
86  {
87  }
88 
89  // Set counter value (not thread-safe).
90  inline void set (integer_t value_)
91  {
92  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  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  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  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
210 #endif
211 
212 #if ! defined ZMQ_ATOMIC_COUNTER_CXX11
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
atomic_counter_t(integer_t value_=0)
void unlock()
Definition: mutex.hpp:136
integer_t add(integer_t increment_)
bool sub(integer_t decrement)
void lock()
Definition: mutex.hpp:120
volatile integer_t value
Definition: address.hpp:35
const atomic_counter_t & operator=(const atomic_counter_t &)