atomic_counter.hpp 8.75 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
Martin Sustrik's avatar
Martin Sustrik committed
3

4
    This file is part of libzmq, the ZeroMQ core engine in C++.
Martin Sustrik's avatar
Martin Sustrik committed
5

6 7 8
    libzmq is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 3 of the License, or
Martin Sustrik's avatar
Martin Sustrik committed
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    As a special exception, the Contributors give you permission to link
    this library with independent modules to produce an executable,
    regardless of the license terms of these independent modules, and to
    copy and distribute the resulting executable under terms of your choice,
    provided that you also meet, for each linked independent module, the
    terms and conditions of the license of that module. An independent
    module is a module which is not derived from or based on this library.
    If you modify this library, you must extend this exception to your
    version of the library.

    libzmq is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
25

26
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
27 28 29
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

Martin Sustrik's avatar
Martin Sustrik committed
30 31
#ifndef __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__
#define __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__
Martin Sustrik's avatar
Martin Sustrik committed
32 33

#include "stdint.hpp"
34
#include "macros.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
35

Martin Sustrik's avatar
Martin Sustrik committed
36 37
#if defined ZMQ_FORCE_MUTEXES
#define ZMQ_ATOMIC_COUNTER_MUTEX
38
#elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
39
#define ZMQ_ATOMIC_COUNTER_INTRINSIC
40
#elif (defined __cplusplus && __cplusplus >= 201103L)                          \
41
  || (defined _MSC_VER && _MSC_VER >= 1900)
42
#define ZMQ_ATOMIC_COUNTER_CXX11
Martin Sustrik's avatar
Martin Sustrik committed
43
#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
Martin Sustrik's avatar
Martin Sustrik committed
44
#define ZMQ_ATOMIC_COUNTER_X86
45 46
#elif defined __ARM_ARCH_7A__ && defined __GNUC__
#define ZMQ_ATOMIC_COUNTER_ARM
Martin Sustrik's avatar
Martin Sustrik committed
47 48
#elif defined ZMQ_HAVE_WINDOWS
#define ZMQ_ATOMIC_COUNTER_WINDOWS
49 50
#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD                     \
       || defined ZMQ_HAVE_GNU)
51
#define ZMQ_ATOMIC_COUNTER_ATOMIC_H
52 53
#elif defined __tile__
#define ZMQ_ATOMIC_COUNTER_TILE
Martin Sustrik's avatar
Martin Sustrik committed
54
#else
Martin Sustrik's avatar
Martin Sustrik committed
55
#define ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
56 57
#endif

Martin Sustrik's avatar
Martin Sustrik committed
58
#if defined ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
59
#include "mutex.hpp"
60 61
#elif defined ZMQ_ATOMIC_COUNTER_CXX11
#include <atomic>
Martin Sustrik's avatar
Martin Sustrik committed
62
#elif defined ZMQ_ATOMIC_COUNTER_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
63
#include "windows.hpp"
64
#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
65
#include <atomic.h>
66 67
#elif defined ZMQ_ATOMIC_COUNTER_TILE
#include <arch/atomic.h>
Martin Sustrik's avatar
Martin Sustrik committed
68 69
#endif

Martin Sustrik's avatar
Martin Sustrik committed
70
namespace zmq
Martin Sustrik's avatar
Martin Sustrik committed
71
{
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
//  This class represents an integer that can be incremented/decremented
//  in atomic fashion.
//
//  In zmq::shared_message_memory_allocator a buffer with an atomic_counter_t
//  at the start is allocated. If the class does not align to pointer size,
//  access to pointers in structures in the buffer will cause SIGBUS on
//  architectures that do not allow mis-aligned pointers (eg: SPARC).
//  Force the compiler to align to pointer size, which will cause the object
//  to grow from 4 bytes to 8 bytes on 64 bit architectures (when not using
//  mutexes).

#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
class __declspec(align (8)) atomic_counter_t
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE))
class __declspec(align (4)) atomic_counter_t
87
#else
88
class atomic_counter_t
89
#endif
90 91 92
{
  public:
    typedef uint32_t integer_t;
Martin Sustrik's avatar
Martin Sustrik committed
93

94 95 96 97
    inline atomic_counter_t (integer_t value_ = 0) ZMQ_NOEXCEPT
        : _value (value_)
    {
    }
Martin Sustrik's avatar
Martin Sustrik committed
98

99
    //  Set counter _value (not thread-safe).
100
    inline void set (integer_t value_) ZMQ_NOEXCEPT { _value = value_; }
Martin Sustrik's avatar
Martin Sustrik committed
101

102
    //  Atomic addition. Returns the old _value.
103
    inline integer_t add (integer_t increment_) ZMQ_NOEXCEPT
104 105
    {
        integer_t old_value;
Martin Sustrik's avatar
Martin Sustrik committed
106

Martin Sustrik's avatar
Martin Sustrik committed
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_);
Martin Sustrik's avatar
Martin Sustrik committed
118
#elif defined ZMQ_ATOMIC_COUNTER_X86
119
        __asm__ volatile("lock; xadd %0, %1 \n\t"
120 121
                         : "=r"(old_value), "=m"(_value)
                         : "0"(increment_), "m"(_value)
122
                         : "cc", "memory");
123
#elif defined ZMQ_ATOMIC_COUNTER_ARM
124 125 126 127 128 129 130 131 132
        integer_t flag, tmp;
        __asm__ volatile("       dmb     sy\n\t"
                         "1:     ldrex   %0, [%5]\n\t"
                         "       add     %2, %0, %4\n\t"
                         "       strex   %1, %2, [%5]\n\t"
                         "       teq     %1, #0\n\t"
                         "       bne     1b\n\t"
                         "       dmb     sy\n\t"
                         : "=&r"(old_value), "=&r"(flag), "=&r"(tmp),
133 134
                           "+Qo"(_value)
                         : "Ir"(increment_), "r"(&_value)
135
                         : "cc");
Martin Sustrik's avatar
Martin Sustrik committed
136
#elif defined ZMQ_ATOMIC_COUNTER_MUTEX
137
        sync.lock ();
138 139
        old_value = _value;
        _value += increment_;
140
        sync.unlock ();
Martin Sustrik's avatar
Martin Sustrik committed
141
#else
142
#error atomic_counter is not implemented for this platform
Martin Sustrik's avatar
Martin Sustrik committed
143
#endif
144 145
        return old_value;
    }
Martin Sustrik's avatar
Martin Sustrik committed
146

147
    //  Atomic subtraction. Returns false if the counter drops to zero.
148
    inline bool sub (integer_t decrement_) ZMQ_NOEXCEPT
149
    {
Martin Sustrik's avatar
Martin Sustrik committed
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 160
        integer_t old =
          _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;
Martin Sustrik's avatar
Martin Sustrik committed
170
#elif defined ZMQ_ATOMIC_COUNTER_X86
171
        integer_t oldval = -decrement_;
172
        volatile integer_t *val = &_value;
173 174 175 176
        __asm__ volatile("lock; xaddl %0,%1"
                         : "=r"(oldval), "=m"(*val)
                         : "0"(oldval), "m"(*val)
                         : "cc", "memory");
177
        return oldval != decrement_;
178
#elif defined ZMQ_ATOMIC_COUNTER_ARM
179 180 181 182 183 184 185 186 187
        integer_t old_value, flag, tmp;
        __asm__ volatile("       dmb     sy\n\t"
                         "1:     ldrex   %0, [%5]\n\t"
                         "       sub     %2, %0, %4\n\t"
                         "       strex   %1, %2, [%5]\n\t"
                         "       teq     %1, #0\n\t"
                         "       bne     1b\n\t"
                         "       dmb     sy\n\t"
                         : "=&r"(old_value), "=&r"(flag), "=&r"(tmp),
188 189
                           "+Qo"(_value)
                         : "Ir"(decrement_), "r"(&_value)
190
                         : "cc");
191
        return old_value - decrement_ != 0;
Martin Sustrik's avatar
Martin Sustrik committed
192
#elif defined ZMQ_ATOMIC_COUNTER_MUTEX
193
        sync.lock ();
194 195
        _value -= decrement_;
        bool result = _value ? true : false;
196 197
        sync.unlock ();
        return result;
Martin Sustrik's avatar
Martin Sustrik committed
198
#else
199
#error atomic_counter is not implemented for this platform
Martin Sustrik's avatar
Martin Sustrik committed
200
#endif
201
    }
Martin Sustrik's avatar
Martin Sustrik committed
202

203
    inline integer_t get () const ZMQ_NOEXCEPT { return _value; }
Martin Sustrik's avatar
Martin Sustrik committed
204

205
  private:
206
#if defined ZMQ_ATOMIC_COUNTER_CXX11
207
    std::atomic<integer_t> _value;
208
#else
209
    volatile integer_t _value;
210 211
#endif

Martin Sustrik's avatar
Martin Sustrik committed
212
#if defined ZMQ_ATOMIC_COUNTER_MUTEX
213
    mutex_t sync;
Martin Sustrik's avatar
Martin Sustrik committed
214 215
#endif

216 217 218
#if !defined ZMQ_ATOMIC_COUNTER_CXX11
    atomic_counter_t (const atomic_counter_t &);
    const atomic_counter_t &operator= (const atomic_counter_t &);
219
#endif
220 221 222 223
#if defined(__GNUC__) || defined(__INTEL_COMPILER)                             \
  || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590)                              \
  || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590)
} __attribute__ ((aligned (sizeof (void *))));
224
#else
225
};
226
#endif
Martin Sustrik's avatar
Martin Sustrik committed
227 228 229
}

//  Remove macros local to this file.
230
#undef ZMQ_ATOMIC_COUNTER_MUTEX
231
#undef ZMQ_ATOMIC_COUNTER_INTRINSIC
232
#undef ZMQ_ATOMIC_COUNTER_CXX11
Martin Sustrik's avatar
Martin Sustrik committed
233
#undef ZMQ_ATOMIC_COUNTER_X86
234
#undef ZMQ_ATOMIC_COUNTER_ARM
235 236 237
#undef ZMQ_ATOMIC_COUNTER_WINDOWS
#undef ZMQ_ATOMIC_COUNTER_ATOMIC_H
#undef ZMQ_ATOMIC_COUNTER_TILE
Martin Sustrik's avatar
Martin Sustrik committed
238 239

#endif