atomic_counter.hpp 8.65 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 __cplusplus && __cplusplus >= 201103L)                          \
39
  || (defined _MSC_VER && _MSC_VER >= 1900)
40
#define ZMQ_ATOMIC_COUNTER_CXX11
41 42
#elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
#define ZMQ_ATOMIC_COUNTER_INTRINSIC
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
    atomic_counter_t (integer_t value_ = 0) ZMQ_NOEXCEPT : _value (value_) {}
Martin Sustrik's avatar
Martin Sustrik committed
95

96
    //  Set counter _value (not thread-safe).
97
    void set (integer_t value_) ZMQ_NOEXCEPT { _value = value_; }
Martin Sustrik's avatar
Martin Sustrik committed
98

99
    //  Atomic addition. Returns the old _value.
100
    integer_t add (integer_t increment_) ZMQ_NOEXCEPT
101 102
    {
        integer_t old_value;
Martin Sustrik's avatar
Martin Sustrik committed
103

Martin Sustrik's avatar
Martin Sustrik committed
104
#if defined ZMQ_ATOMIC_COUNTER_WINDOWS
105
        old_value = InterlockedExchangeAdd ((LONG *) &_value, increment_);
106
#elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC
107
        old_value = __atomic_fetch_add (&_value, increment_, __ATOMIC_ACQ_REL);
108
#elif defined ZMQ_ATOMIC_COUNTER_CXX11
109
        old_value = _value.fetch_add (increment_, std::memory_order_acq_rel);
110
#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
111
        integer_t new_value = atomic_add_32_nv (&_value, increment_);
112
        old_value = new_value - increment_;
113
#elif defined ZMQ_ATOMIC_COUNTER_TILE
114
        old_value = arch_atomic_add (&_value, increment_);
Martin Sustrik's avatar
Martin Sustrik committed
115
#elif defined ZMQ_ATOMIC_COUNTER_X86
116
        __asm__ volatile("lock; xadd %0, %1 \n\t"
117 118
                         : "=r"(old_value), "=m"(_value)
                         : "0"(increment_), "m"(_value)
119
                         : "cc", "memory");
120
#elif defined ZMQ_ATOMIC_COUNTER_ARM
121 122 123 124 125 126 127 128 129
        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),
130 131
                           "+Qo"(_value)
                         : "Ir"(increment_), "r"(&_value)
132
                         : "cc");
Martin Sustrik's avatar
Martin Sustrik committed
133
#elif defined ZMQ_ATOMIC_COUNTER_MUTEX
134
        sync.lock ();
135 136
        old_value = _value;
        _value += increment_;
137
        sync.unlock ();
Martin Sustrik's avatar
Martin Sustrik committed
138
#else
139
#error atomic_counter is not implemented for this platform
Martin Sustrik's avatar
Martin Sustrik committed
140
#endif
141 142
        return old_value;
    }
Martin Sustrik's avatar
Martin Sustrik committed
143

144
    //  Atomic subtraction. Returns false if the counter drops to zero.
145
    bool sub (integer_t decrement_) ZMQ_NOEXCEPT
146
    {
Martin Sustrik's avatar
Martin Sustrik committed
147
#if defined ZMQ_ATOMIC_COUNTER_WINDOWS
148
        LONG delta = -((LONG) decrement_);
149
        integer_t old = InterlockedExchangeAdd ((LONG *) &_value, delta);
150
        return old - decrement_ != 0;
151
#elif defined ZMQ_ATOMIC_COUNTER_INTRINSIC
152
        integer_t nv =
153
          __atomic_sub_fetch (&_value, decrement_, __ATOMIC_ACQ_REL);
154
        return nv != 0;
155
#elif defined ZMQ_ATOMIC_COUNTER_CXX11
156
        const integer_t old =
157
          _value.fetch_sub (decrement_, std::memory_order_acq_rel);
158
        return old - decrement_ != 0;
159
#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
160
        int32_t delta = -((int32_t) decrement_);
161
        integer_t nv = atomic_add_32_nv (&_value, delta);
162
        return nv != 0;
163
#elif defined ZMQ_ATOMIC_COUNTER_TILE
164
        int32_t delta = -((int32_t) decrement_);
165
        integer_t nv = arch_atomic_add (&_value, delta);
166
        return nv != 0;
Martin Sustrik's avatar
Martin Sustrik committed
167
#elif defined ZMQ_ATOMIC_COUNTER_X86
168
        integer_t oldval = -decrement_;
169
        volatile integer_t *val = &_value;
170 171 172 173
        __asm__ volatile("lock; xaddl %0,%1"
                         : "=r"(oldval), "=m"(*val)
                         : "0"(oldval), "m"(*val)
                         : "cc", "memory");
174
        return oldval != decrement_;
175
#elif defined ZMQ_ATOMIC_COUNTER_ARM
176 177 178 179 180 181 182 183 184
        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),
185 186
                           "+Qo"(_value)
                         : "Ir"(decrement_), "r"(&_value)
187
                         : "cc");
188
        return old_value - decrement_ != 0;
Martin Sustrik's avatar
Martin Sustrik committed
189
#elif defined ZMQ_ATOMIC_COUNTER_MUTEX
190
        sync.lock ();
191 192
        _value -= decrement_;
        bool result = _value ? true : false;
193 194
        sync.unlock ();
        return result;
Martin Sustrik's avatar
Martin Sustrik committed
195
#else
196
#error atomic_counter is not implemented for this platform
Martin Sustrik's avatar
Martin Sustrik committed
197
#endif
198
    }
Martin Sustrik's avatar
Martin Sustrik committed
199

200
    integer_t get () const ZMQ_NOEXCEPT { return _value; }
Martin Sustrik's avatar
Martin Sustrik committed
201

202
  private:
203
#if defined ZMQ_ATOMIC_COUNTER_CXX11
204
    std::atomic<integer_t> _value;
205
#else
206
    volatile integer_t _value;
207 208
#endif

Martin Sustrik's avatar
Martin Sustrik committed
209
#if defined ZMQ_ATOMIC_COUNTER_MUTEX
210
    mutex_t sync;
Martin Sustrik's avatar
Martin Sustrik committed
211 212
#endif

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

//  Remove macros local to this file.
226
#undef ZMQ_ATOMIC_COUNTER_MUTEX
227
#undef ZMQ_ATOMIC_COUNTER_INTRINSIC
228
#undef ZMQ_ATOMIC_COUNTER_CXX11
Martin Sustrik's avatar
Martin Sustrik committed
229
#undef ZMQ_ATOMIC_COUNTER_X86
230
#undef ZMQ_ATOMIC_COUNTER_ARM
231 232 233
#undef ZMQ_ATOMIC_COUNTER_WINDOWS
#undef ZMQ_ATOMIC_COUNTER_ATOMIC_H
#undef ZMQ_ATOMIC_COUNTER_TILE
Martin Sustrik's avatar
Martin Sustrik committed
234 235

#endif