atomic_ptr.hpp 9.25 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_PTR_HPP_INCLUDED__
#define __ZMQ_ATOMIC_PTR_HPP_INCLUDED__
Martin Sustrik's avatar
Martin Sustrik committed
32

33 34
#include "macros.hpp"

Martin Sustrik's avatar
Martin Sustrik committed
35 36
#if defined ZMQ_FORCE_MUTEXES
#define ZMQ_ATOMIC_PTR_MUTEX
37 38
#elif (defined __cplusplus && __cplusplus >= 201103L)                          \
  || (defined _MSC_VER && _MSC_VER >= 1900)
39
#define ZMQ_ATOMIC_PTR_CXX11
40 41
#elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
#define ZMQ_ATOMIC_PTR_INTRINSIC
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_PTR_X86
44 45
#elif defined __ARM_ARCH_7A__ && defined __GNUC__
#define ZMQ_ATOMIC_PTR_ARM
46 47
#elif defined __tile__
#define ZMQ_ATOMIC_PTR_TILE
Martin Sustrik's avatar
Martin Sustrik committed
48 49
#elif defined ZMQ_HAVE_WINDOWS
#define ZMQ_ATOMIC_PTR_WINDOWS
50 51
#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD                     \
       || defined ZMQ_HAVE_GNU)
52
#define ZMQ_ATOMIC_PTR_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
53
#else
Martin Sustrik's avatar
Martin Sustrik committed
54
#define ZMQ_ATOMIC_PTR_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
55 56
#endif

Martin Sustrik's avatar
Martin Sustrik committed
57
#if defined ZMQ_ATOMIC_PTR_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
58
#include "mutex.hpp"
59
#elif defined ZMQ_ATOMIC_PTR_CXX11
60
#include <atomic>
Martin Sustrik's avatar
Martin Sustrik committed
61
#elif defined ZMQ_ATOMIC_PTR_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
62
#include "windows.hpp"
63
#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
64
#include <atomic.h>
65 66
#elif defined ZMQ_ATOMIC_PTR_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
#if !defined ZMQ_ATOMIC_PTR_CXX11
72
inline void *atomic_xchg_ptr (void **ptr_,
73 74 75
                              void *const val_
#if defined ZMQ_ATOMIC_PTR_MUTEX
                              ,
76
                              mutex_t &_sync
77
#endif
78
                              ) ZMQ_NOEXCEPT
79 80
{
#if defined ZMQ_ATOMIC_PTR_WINDOWS
81
    return InterlockedExchangePointer ((PVOID *) ptr_, val_);
82
#elif defined ZMQ_ATOMIC_PTR_INTRINSIC
83
    return __atomic_exchange_n (ptr_, val_, __ATOMIC_ACQ_REL);
84
#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
85
    return atomic_swap_ptr (ptr_, val_);
86
#elif defined ZMQ_ATOMIC_PTR_TILE
87
    return arch_atomic_exchange (ptr_, val_);
88 89 90
#elif defined ZMQ_ATOMIC_PTR_X86
    void *old;
    __asm__ volatile("lock; xchg %0, %2"
91 92
                     : "=r"(old), "=m"(*ptr_)
                     : "m"(*ptr_), "0"(val_));
93 94 95 96 97 98 99 100 101 102
    return old;
#elif defined ZMQ_ATOMIC_PTR_ARM
    void *old;
    unsigned int flag;
    __asm__ volatile("       dmb     sy\n\t"
                     "1:     ldrex   %1, [%3]\n\t"
                     "       strex   %0, %4, [%3]\n\t"
                     "       teq     %0, #0\n\t"
                     "       bne     1b\n\t"
                     "       dmb     sy\n\t"
103 104
                     : "=&r"(flag), "=&r"(old), "+Qo"(*ptr_)
                     : "r"(ptr_), "r"(val_)
105 106 107
                     : "cc");
    return old;
#elif defined ZMQ_ATOMIC_PTR_MUTEX
108 109 110 111
    _sync.lock ();
    void *old = *ptr_;
    *ptr_ = val_;
    _sync.unlock ();
112 113 114 115 116 117 118 119 120 121 122
    return old;
#else
#error atomic_ptr is not implemented for this platform
#endif
}

inline void *atomic_cas (void *volatile *ptr_,
                         void *cmp_,
                         void *val_
#if defined ZMQ_ATOMIC_PTR_MUTEX
                         ,
123
                         mutex_t &_sync
124
#endif
125
                         ) ZMQ_NOEXCEPT
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
{
#if defined ZMQ_ATOMIC_PTR_WINDOWS
    return InterlockedCompareExchangePointer ((volatile PVOID *) ptr_, val_,
                                              cmp_);
#elif defined ZMQ_ATOMIC_PTR_INTRINSIC
    void *old = cmp_;
    __atomic_compare_exchange_n (ptr_, &old, val_, false, __ATOMIC_RELEASE,
                                 __ATOMIC_ACQUIRE);
    return old;
#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
    return atomic_cas_ptr (ptr_, cmp_, val_);
#elif defined ZMQ_ATOMIC_PTR_TILE
    return arch_atomic_val_compare_and_exchange (ptr_, cmp_, val_);
#elif defined ZMQ_ATOMIC_PTR_X86
    void *old;
    __asm__ volatile("lock; cmpxchg %2, %3"
                     : "=a"(old), "=m"(*ptr_)
                     : "r"(val_), "m"(*ptr_), "0"(cmp_)
                     : "cc");
    return old;
#elif defined ZMQ_ATOMIC_PTR_ARM
    void *old;
    unsigned int flag;
    __asm__ volatile("       dmb     sy\n\t"
                     "1:     ldrex   %1, [%3]\n\t"
                     "       mov     %0, #0\n\t"
                     "       teq     %1, %4\n\t"
                     "       it      eq\n\t"
                     "       strexeq %0, %5, [%3]\n\t"
                     "       teq     %0, #0\n\t"
                     "       bne     1b\n\t"
                     "       dmb     sy\n\t"
                     : "=&r"(flag), "=&r"(old), "+Qo"(*ptr_)
                     : "r"(ptr_), "r"(cmp_), "r"(val_)
                     : "cc");
    return old;
#elif defined ZMQ_ATOMIC_PTR_MUTEX
163
    _sync.lock ();
164 165 166
    void *old = *ptr_;
    if (*ptr_ == cmp_)
        *ptr_ = val_;
167
    _sync.unlock ();
168 169 170 171 172 173 174
    return old;
#else
#error atomic_ptr is not implemented for this platform
#endif
}
#endif

175
//  This class encapsulates several atomic operations on pointers.
Martin Sustrik's avatar
Martin Sustrik committed
176

177 178 179 180
template <typename T> class atomic_ptr_t
{
  public:
    //  Initialise atomic pointer
181
    atomic_ptr_t () ZMQ_NOEXCEPT { _ptr = NULL; }
182 183 184 185

    //  Set value of atomic pointer in a non-threadsafe way
    //  Use this function only when you are sure that at most one
    //  thread is accessing the pointer at the moment.
186
    void set (T *ptr_) ZMQ_NOEXCEPT { _ptr = ptr_; }
Martin Sustrik's avatar
Martin Sustrik committed
187

188
    //  Perform atomic 'exchange pointers' operation. Pointer is set
189
    //  to the 'val_' value. Old value is returned.
190
    T *xchg (T *val_) ZMQ_NOEXCEPT
Martin Sustrik's avatar
Martin Sustrik committed
191
    {
192
#if defined ZMQ_ATOMIC_PTR_CXX11
193
        return _ptr.exchange (val_, std::memory_order_acq_rel);
Martin Sustrik's avatar
Martin Sustrik committed
194
#else
195
        return (T *) atomic_xchg_ptr ((void **) &_ptr, val_
196 197
#if defined ZMQ_ATOMIC_PTR_MUTEX
                                      ,
198
                                      _sync
199 200
#endif
        );
Martin Sustrik's avatar
Martin Sustrik committed
201
#endif
202 203 204 205
    }

    //  Perform atomic 'compare and swap' operation on the pointer.
    //  The pointer is compared to 'cmp' argument and if they are
206
    //  equal, its value is set to 'val_'. Old value of the pointer
207
    //  is returned.
208
    T *cas (T *cmp_, T *val_) ZMQ_NOEXCEPT
209
    {
210
#if defined ZMQ_ATOMIC_PTR_CXX11
211
        _ptr.compare_exchange_strong (cmp_, val_, std::memory_order_acq_rel);
212
        return cmp_;
Martin Sustrik's avatar
Martin Sustrik committed
213
#else
214
        return (T *) atomic_cas ((void **) &_ptr, cmp_, val_
215 216
#if defined ZMQ_ATOMIC_PTR_MUTEX
                                 ,
217
                                 _sync
218 219
#endif
        );
Martin Sustrik's avatar
Martin Sustrik committed
220
#endif
221
    }
Martin Sustrik's avatar
Martin Sustrik committed
222

223
  private:
224
#if defined ZMQ_ATOMIC_PTR_CXX11
225
    std::atomic<T *> _ptr;
226
#else
227
    volatile T *_ptr;
228 229
#endif

Martin Sustrik's avatar
Martin Sustrik committed
230
#if defined ZMQ_ATOMIC_PTR_MUTEX
231
    mutex_t _sync;
Martin Sustrik's avatar
Martin Sustrik committed
232 233
#endif

234
#if !defined ZMQ_ATOMIC_PTR_CXX11
235
    ZMQ_NON_COPYABLE_NOR_MOVABLE (atomic_ptr_t)
236
#endif
237
};
238 239 240

struct atomic_value_t
{
241
    atomic_value_t (const int value_) ZMQ_NOEXCEPT : _value (value_) {}
242

243 244 245 246
    atomic_value_t (const atomic_value_t &src_) ZMQ_NOEXCEPT
        : _value (src_.load ())
    {
    }
247

248
    void store (const int value_) ZMQ_NOEXCEPT
249 250
    {
#if defined ZMQ_ATOMIC_PTR_CXX11
251
        _value.store (value_, std::memory_order_release);
252
#else
253
        atomic_xchg_ptr ((void **) &_value, (void *) (ptrdiff_t) value_
254 255
#if defined ZMQ_ATOMIC_PTR_MUTEX
                         ,
256
                         _sync
257 258 259 260 261
#endif
        );
#endif
    }

262
    int load () const ZMQ_NOEXCEPT
263 264
    {
#if defined ZMQ_ATOMIC_PTR_CXX11
265
        return _value.load (std::memory_order_acquire);
266
#else
267
        return (int) (ptrdiff_t) atomic_cas ((void **) &_value, 0, 0
268 269
#if defined ZMQ_ATOMIC_PTR_MUTEX
                                             ,
270
#if defined __SUNPRO_CC
271
                                             const_cast<mutex_t &> (_sync)
272
#else
273
                                             _sync
274
#endif
275 276 277 278 279 280 281
#endif
        );
#endif
    }

  private:
#if defined ZMQ_ATOMIC_PTR_CXX11
282
    std::atomic<int> _value;
283
#else
284
    volatile ptrdiff_t _value;
285 286 287
#endif

#if defined ZMQ_ATOMIC_PTR_MUTEX
288
    mutable mutex_t _sync;
289 290 291
#endif

  private:
292
    atomic_value_t &operator= (const atomic_value_t &src_);
293
};
Martin Sustrik's avatar
Martin Sustrik committed
294 295 296
}

//  Remove macros local to this file.
297 298
#undef ZMQ_ATOMIC_PTR_MUTEX
#undef ZMQ_ATOMIC_PTR_INTRINSIC
299
#undef ZMQ_ATOMIC_PTR_CXX11
Martin Sustrik's avatar
Martin Sustrik committed
300
#undef ZMQ_ATOMIC_PTR_X86
301
#undef ZMQ_ATOMIC_PTR_ARM
302 303 304
#undef ZMQ_ATOMIC_PTR_TILE
#undef ZMQ_ATOMIC_PTR_WINDOWS
#undef ZMQ_ATOMIC_PTR_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
305 306

#endif