atomic_bitmap.hpp 9.36 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2010 iMatix Corporation
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
    the terms of the Lesser GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ 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
    Lesser GNU General Public License for more details.

    You should have received a copy of the Lesser GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

Martin Sustrik's avatar
Martin Sustrik committed
20 21
#ifndef __ZMQ_ATOMIC_BITMAP_HPP_INCLUDED__
#define __ZMQ_ATOMIC_BITMAP_HPP_INCLUDED__
Martin Sustrik's avatar
Martin Sustrik committed
22 23 24 25 26 27 28

#include "stdint.hpp"
#include "platform.hpp"

//  These are the conditions to choose between different implementations
//  of atomic_bitmap.

Martin Sustrik's avatar
Martin Sustrik committed
29 30
#if defined ZMQ_FORCE_MUTEXES
#define ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
31
#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
Martin Sustrik's avatar
Martin Sustrik committed
32
#define ZMQ_ATOMIC_BITMAP_X86
Martin Sustrik's avatar
Martin Sustrik committed
33
#elif 0 && defined __sparc__ && defined __GNUC__
Martin Sustrik's avatar
Martin Sustrik committed
34 35 36 37 38
#define ZMQ_ATOMIC_BITMAP_SPARC
#elif defined ZMQ_HAVE_WINDOWS
#define ZMQ_ATOMIC_BITMAP_WINDOWS
#elif defined ZMQ_HAVE_SOLARIS
#define ZMQ_ATOMIC_BITMAP_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
39
#else
Martin Sustrik's avatar
Martin Sustrik committed
40
#define ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
41 42
#endif

Martin Sustrik's avatar
Martin Sustrik committed
43
#if defined ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
44
#include "mutex.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
45
#elif defined ZMQ_ATOMIC_BITMAP_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
46
#include "windows.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
47
#elif defined ZMQ_ATOMIC_BITMAP_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
48 49 50
#include <atomic.h>
#endif

Martin Sustrik's avatar
Martin Sustrik committed
51
namespace zmq
Martin Sustrik's avatar
Martin Sustrik committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
{

    //  This class encapuslates several bitwise atomic operations on unsigned
    //  integer. Selection of operations is driven specifically by the needs
    //  of ypollset implementation.

    class atomic_bitmap_t
    {
    public:

#if (defined ZMQ_ATOMIC_BITMAP_X86 || defined ZMQ_FORCE_MUTEXES) \
    && defined __x86_64__
        typedef uint64_t bitmap_t;
#else
        typedef uint32_t bitmap_t;
#endif

        inline atomic_bitmap_t (bitmap_t value_ = 0) :
            value (value_)
        {
        }

        inline ~atomic_bitmap_t ()
        {
        }

        //  Bit-test-set-and-reset. Sets one bit of the value and resets
        //  another one. Returns the original value of the reset bit.
        inline bool btsr (int set_index_, int reset_index_)
        {
Martin Sustrik's avatar
Martin Sustrik committed
82
#if defined ZMQ_ATOMIC_BITMAP_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
83 84 85 86 87 88 89 90 91
            while (true) {
                bitmap_t oldval = value;
                bitmap_t newval = (oldval | (bitmap_t (1) << set_index_)) &
                    ~(bitmap_t (1) << reset_index_);
                if (InterlockedCompareExchange ((volatile LONG*) &value, newval,
                      oldval) == (LONG) oldval)
                    return (oldval & (bitmap_t (1) << reset_index_)) ?
                        true : false;
            }
Martin Sustrik's avatar
Martin Sustrik committed
92
#elif defined ZMQ_ATOMIC_BITMAP_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
93 94 95 96 97 98 99 100
            while (true) {
                bitmap_t oldval = value;
                bitmap_t newval = (oldval | (bitmap_t (1) << set_index_)) &
                    ~(bitmap_t (1) << reset_index_);
                if (atomic_cas_32 (&value, oldval, newval) == oldval)
                    return (oldval & (bitmap_t (1) << reset_index_)) ?
                        true : false;
            }
Martin Sustrik's avatar
Martin Sustrik committed
101
#elif defined ZMQ_ATOMIC_BITMAP_X86
Martin Sustrik's avatar
Martin Sustrik committed
102 103 104 105 106 107 108 109 110 111 112 113 114
            bitmap_t oldval, dummy;
            __asm__ volatile (
                "mov %0, %1\n\t"
                "1:"
                "mov %1, %2\n\t"
                "bts %3, %2\n\t"
                "btr %4, %2\n\t"
                "lock cmpxchg %2, %0\n\t"
                "jnz 1b\n\t"
                : "+m" (value), "=&a" (oldval), "=&r" (dummy)
                : "r" (bitmap_t(set_index_)), "r" (bitmap_t(reset_index_))
                : "cc");
            return (bool) (oldval & (bitmap_t(1) << reset_index_));
Martin Sustrik's avatar
Martin Sustrik committed
115
#elif defined ZMQ_ATOMIC_BITMAP_SPARC
Martin Sustrik's avatar
Martin Sustrik committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
            volatile bitmap_t* valptr = &value;
            bitmap_t set_val = bitmap_t(1) << set_index_;
            bitmap_t reset_val = ~(bitmap_t(1) << reset_index_);
            bitmap_t tmp;
            bitmap_t oldval;
            __asm__ volatile(
                "ld       [%5], %2       \n\t"
                "1:                      \n\t"
                "or       %2, %0, %3     \n\t"
                "and      %3, %1, %3     \n\t"
                "cas      [%5], %2, %3   \n\t"
                "cmp      %2, %3         \n\t"
                "bne,a,pn %%icc, 1b      \n\t"
                "mov      %3, %2         \n\t"
                : "+r" (set_val), "+r" (reset_val), "=&r" (tmp),
                  "=&r" (oldval), "+m" (*valptr)
                : "r" (valptr)
                : "cc");
            return oldval;
Martin Sustrik's avatar
Martin Sustrik committed
135
#elif defined ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
            sync.lock ();
            bitmap_t oldval = value;
            value = (oldval | (bitmap_t (1) << set_index_)) &
                ~(bitmap_t (1) << reset_index_);
            sync.unlock ();
            return (oldval & (bitmap_t (1) << reset_index_)) ? true : false;
#else
#error
#endif
        }

        //  Sets value to newval. Returns the original value.
        inline bitmap_t xchg (bitmap_t newval_)
        {
            bitmap_t oldval;
Martin Sustrik's avatar
Martin Sustrik committed
151
#if defined ZMQ_ATOMIC_BITMAP_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
152
            oldval = InterlockedExchange ((volatile LONG*) &value, newval_);
Martin Sustrik's avatar
Martin Sustrik committed
153
#elif defined ZMQ_ATOMIC_BITMAP_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
154
            oldval = atomic_swap_32 (&value, newval_);
Martin Sustrik's avatar
Martin Sustrik committed
155
#elif defined ZMQ_ATOMIC_BITMAP_X86
Martin Sustrik's avatar
Martin Sustrik committed
156 157 158 159 160 161
            oldval = newval_;
            __asm__ volatile (
                "lock; xchg %0, %1"
                : "=r" (oldval)
                : "m" (value), "0" (oldval)
                : "memory");
Martin Sustrik's avatar
Martin Sustrik committed
162
#elif defined ZMQ_ATOMIC_BITMAP_SPARC
Martin Sustrik's avatar
Martin Sustrik committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
            oldval = value;
            volatile bitmap_t* ptrin = &value;
            bitmap_t tmp;
            bitmap_t prev;
            __asm__ __volatile__(
                "ld [%4], %1\n\t"
                "1:\n\t"
                "mov %0, %2\n\t"
                "cas [%4], %1, %2\n\t"
                "cmp %1, %2\n\t"
                "bne,a,pn %%icc, 1b\n\t"
                "mov %2, %1\n\t"
                : "+r" (newval_), "=&r" (tmp), "=&r" (prev), "+m" (*ptrin)
                : "r" (ptrin)
                : "cc");
            return prev;
Martin Sustrik's avatar
Martin Sustrik committed
179
#elif defined ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
            sync.lock ();
            oldval = value;
            value = newval_;
            sync.unlock ();
#else
#error
#endif
            return oldval;
        }

        //  izte is "if-zero-then-else" atomic operation - if the value is zero
        //  it substitutes it by 'thenval' else it rewrites it by 'elseval'.
        //  Original value of the integer is returned from this function.
        inline bitmap_t izte (bitmap_t thenval_,
            bitmap_t elseval_)
        {
Martin Sustrik's avatar
Martin Sustrik committed
196
#if defined ZMQ_ATOMIC_BITMAP_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
197 198 199 200 201 202 203
            while (true) {
                bitmap_t oldval = value;
                bitmap_t newval = oldval == 0 ? thenval_ : elseval_;
                if (InterlockedCompareExchange ((volatile LONG*) &value,
                      newval, oldval) == (LONG) oldval)
                    return oldval;
            }
Martin Sustrik's avatar
Martin Sustrik committed
204
#elif defined ZMQ_ATOMIC_BITMAP_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
205 206 207 208 209 210
            while (true) {
                bitmap_t oldval = value;
                bitmap_t newval = oldval == 0 ? thenval_ : elseval_;
                if (atomic_cas_32 (&value, oldval, newval) == oldval)
                    return oldval;
            }
Martin Sustrik's avatar
Martin Sustrik committed
211
#elif defined ZMQ_ATOMIC_BITMAP_X86
Martin Sustrik's avatar
Martin Sustrik committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
            bitmap_t oldval;
            bitmap_t dummy;
            __asm__ volatile (
                "mov %0, %1\n\t"
                "1:"
                "mov %3, %2\n\t"
                "test %1, %1\n\t"
                "jz 2f\n\t"
                "mov %4, %2\n\t"
                "2:"
                "lock cmpxchg %2, %0\n\t"
                "jnz 1b\n\t"
                : "+m" (value), "=&a" (oldval), "=&r" (dummy)
                : "r" (thenval_), "r" (elseval_)
                : "cc");
            return oldval;
Martin Sustrik's avatar
Martin Sustrik committed
228
#elif defined ZMQ_ATOMIC_BITMAP_SPARC
Martin Sustrik's avatar
Martin Sustrik committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
            volatile bitmap_t* ptrin = &value;
            bitmap_t tmp;
            bitmap_t prev;
            __asm__ __volatile__(
                "ld      [%3], %0       \n\t"
                "mov     0,    %1       \n\t"
                "cas     [%3], %1, %4   \n\t"
                "cmp     %0,   %1       \n\t"
                "be,a,pn %%icc,1f       \n\t"
                "ld      [%3], %0       \n\t"
                "cas     [%3], %0, %5   \n\t"
                "1:                     \n\t"
                : "=&r" (tmp), "=&r" (prev), "+m" (*ptrin)
                : "r" (ptrin), "r" (thenval_), "r" (elseval_)
                : "cc");
            return prev;
Martin Sustrik's avatar
Martin Sustrik committed
245
#elif defined ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
246 247 248 249 250 251 252 253 254 255 256 257 258
            sync.lock ();
            bitmap_t oldval = value;
            value = oldval ? elseval_ : thenval_;
            sync.unlock ();
            return oldval;
#else
#error
#endif
        }

    private:

        volatile bitmap_t value;
Martin Sustrik's avatar
Martin Sustrik committed
259
#if defined ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
260 261 262 263 264 265 266 267 268 269
        mutex_t sync;
#endif

        atomic_bitmap_t (const atomic_bitmap_t&);
        void operator = (const atomic_bitmap_t&);
    };

}

//  Remove macros local to this file.
Martin Sustrik's avatar
Martin Sustrik committed
270 271
#if defined ZMQ_ATOMIC_BITMAP_WINDOWS
#undef ZMQ_ATOMIC_BITMAP_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
272
#endif
Martin Sustrik's avatar
Martin Sustrik committed
273 274
#if defined ZMQ_ATOMIC_BITMAP_SOLARIS
#undef ZMQ_ATOMIC_BITMAP_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
275
#endif
Martin Sustrik's avatar
Martin Sustrik committed
276 277
#if defined ZMQ_ATOMIC_BITMAP_X86
#undef ZMQ_ATOMIC_BITMAP_X86
Martin Sustrik's avatar
Martin Sustrik committed
278
#endif
Martin Sustrik's avatar
Martin Sustrik committed
279 280
#if defined ZMQ_ATOMIC_BITMAP_SPARC
#undef ZMQ_ATOMIC_BITMAP_SPARC
Martin Sustrik's avatar
Martin Sustrik committed
281
#endif
Martin Sustrik's avatar
Martin Sustrik committed
282 283
#if defined ZMQ_ATOMIC_BITMAP_MUTEX
#undef ZMQ_ATOMIC_BITMAP_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
284 285 286
#endif

#endif