atomic_counter.hpp 8.69 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 34

#include "stdint.hpp"

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

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

Martin Sustrik's avatar
Martin Sustrik committed
69
namespace zmq
Martin Sustrik's avatar
Martin Sustrik committed
70
{
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
//  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
86
#else
87
class atomic_counter_t
88
#endif
89 90 91
{
  public:
    typedef uint32_t integer_t;
Martin Sustrik's avatar
Martin Sustrik committed
92

93
    inline atomic_counter_t (integer_t value_ = 0) : _value (value_) {}
Martin Sustrik's avatar
Martin Sustrik committed
94

95
    inline ~atomic_counter_t () {}
Martin Sustrik's avatar
Martin Sustrik committed
96

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

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

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

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

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

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

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

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

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

#endif