atomic_counter.hpp 4.58 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

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
7
    the terms of the GNU Lesser General Public License as published by
Martin Sustrik's avatar
Martin Sustrik committed
8 9 10 11 12 13
    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
14
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
15

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

Martin Sustrik's avatar
Martin Sustrik committed
20 21
#ifndef __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__
#define __ZMQ_ATOMIC_COUNTER_HPP_INCLUDED__
Martin Sustrik's avatar
Martin Sustrik committed
22 23 24 25

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

Martin Sustrik's avatar
Martin Sustrik committed
26 27
#if defined ZMQ_FORCE_MUTEXES
#define ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
28
#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
Martin Sustrik's avatar
Martin Sustrik committed
29 30 31
#define ZMQ_ATOMIC_COUNTER_X86
#elif defined ZMQ_HAVE_WINDOWS
#define ZMQ_ATOMIC_COUNTER_WINDOWS
32 33
#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD)
#define ZMQ_ATOMIC_COUNTER_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
34
#else
Martin Sustrik's avatar
Martin Sustrik committed
35
#define ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
36 37
#endif

Martin Sustrik's avatar
Martin Sustrik committed
38
#if defined ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
39
#include "mutex.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
40
#elif defined ZMQ_ATOMIC_COUNTER_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
41
#include "windows.hpp"
42
#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
43 44 45
#include <atomic.h>
#endif

Martin Sustrik's avatar
Martin Sustrik committed
46
namespace zmq
Martin Sustrik's avatar
Martin Sustrik committed
47 48 49 50 51 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
{

    //  This class represents an integer that can be incremented/decremented
    //  in atomic fashion.

    class atomic_counter_t
    {
    public:

        typedef uint32_t integer_t;

        inline atomic_counter_t (integer_t value_ = 0) :
            value (value_)
        {
        }

        inline ~atomic_counter_t ()
        {
        }

        //  Set counter value (not thread-safe).
        inline void set (integer_t value_)
        {
            value = value_;
        }

        //  Atomic addition. Returns the old value.
        inline integer_t add (integer_t increment_)
        {
            integer_t old_value;

Martin Sustrik's avatar
Martin Sustrik committed
78
#if defined ZMQ_ATOMIC_COUNTER_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
79
            old_value = InterlockedExchangeAdd ((LONG*) &value, increment_);
80
#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
81 82
            integer_t new_value = atomic_add_32_nv (&value, increment_);
            old_value = new_value - increment_;
Martin Sustrik's avatar
Martin Sustrik committed
83
#elif defined ZMQ_ATOMIC_COUNTER_X86
Martin Sustrik's avatar
Martin Sustrik committed
84
            __asm__ volatile (
85
                "lock; xadd %0, %1 \n\t"
Martin Sustrik's avatar
Martin Sustrik committed
86 87 88
                : "=r" (old_value), "=m" (value)
                : "0" (increment_), "m" (value)
                : "cc", "memory");
Martin Sustrik's avatar
Martin Sustrik committed
89
#elif defined ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
90 91 92 93 94
            sync.lock ();
            old_value = value;
            value += increment_;
            sync.unlock ();
#else
95
#error atomic_counter is not implemented for this platform
Martin Sustrik's avatar
Martin Sustrik committed
96 97 98 99 100 101 102
#endif
            return old_value;
        }

        //  Atomic subtraction. Returns false if the counter drops to zero.
        inline bool sub (integer_t decrement)
        {
Martin Sustrik's avatar
Martin Sustrik committed
103
#if defined ZMQ_ATOMIC_COUNTER_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
104 105 106
            LONG delta = - ((LONG) decrement);
            integer_t old = InterlockedExchangeAdd ((LONG*) &value, delta);
            return old - decrement != 0;
107
#elif defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
108 109 110
            int32_t delta = - ((int32_t) decrement);
            integer_t nv = atomic_add_32_nv (&value, delta);
            return nv != 0;
Martin Sustrik's avatar
Martin Sustrik committed
111
#elif defined ZMQ_ATOMIC_COUNTER_X86
Martin Sustrik's avatar
Martin Sustrik committed
112 113 114 115 116
            integer_t oldval = -decrement;
            volatile integer_t *val = &value;
            __asm__ volatile ("lock; xaddl %0,%1"
                : "=r" (oldval), "=m" (*val)
                : "0" (oldval), "m" (*val)
117
                : "cc", "memory");
Martin Sustrik's avatar
Martin Sustrik committed
118
            return oldval != decrement;
Martin Sustrik's avatar
Martin Sustrik committed
119
#elif defined ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
120 121 122 123 124 125
            sync.lock ();
            value -= decrement;
            bool result = value ? true : false;
            sync.unlock ();
            return result;
#else
126
#error atomic_counter is not implemented for this platform
Martin Sustrik's avatar
Martin Sustrik committed
127 128 129 130 131 132 133 134 135 136 137
#endif
        }

        inline integer_t get ()
        {
            return value;
        }

    private:

        volatile integer_t value;
Martin Sustrik's avatar
Martin Sustrik committed
138
#if defined ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
139 140 141 142 143 144 145 146 147 148
        mutex_t sync;
#endif

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

}

//  Remove macros local to this file.
Martin Sustrik's avatar
Martin Sustrik committed
149 150
#if defined ZMQ_ATOMIC_COUNTER_WINDOWS
#undef ZMQ_ATOMIC_COUNTER_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
151
#endif
152 153
#if defined ZMQ_ATOMIC_COUNTER_ATOMIC_H
#undef ZMQ_ATOMIC_COUNTER_ATOMIC_H
Martin Sustrik's avatar
Martin Sustrik committed
154
#endif
Martin Sustrik's avatar
Martin Sustrik committed
155 156
#if defined ZMQ_ATOMIC_COUNTER_X86
#undef ZMQ_ATOMIC_COUNTER_X86
Martin Sustrik's avatar
Martin Sustrik committed
157
#endif
Martin Sustrik's avatar
Martin Sustrik committed
158 159
#if defined ZMQ_ATOMIC_COUNTER_MUTEX
#undef ZMQ_ATOMIC_COUNTER_MUTEX
Martin Sustrik's avatar
Martin Sustrik committed
160 161 162
#endif

#endif
163