Files
NE_YuR/openflow/include/click/atomic.hh

653 lines
17 KiB
C++

// -*- c-basic-offset: 4 -*-
#ifndef CLICK_ATOMIC_HH
#define CLICK_ATOMIC_HH
#if CLICK_LINUXMODULE
# include <click/glue.hh>
#endif
CLICK_DECLS
#if CLICK_LINUXMODULE
# if HAVE_LINUX_ASM_SYSTEM_H
# include <click/cxxprotect.h>
CLICK_CXX_PROTECT
# include <asm/system.h>
CLICK_CXX_UNPROTECT
# include <click/cxxunprotect.h>
# endif
# define CLICK_ATOMIC_VAL _val.counter
#else
# define CLICK_ATOMIC_VAL _val
#endif
#if defined(__i386__) || defined(__arch_um__) || defined(__x86_64__)
# if CLICK_LINUXMODULE || HAVE_MULTITHREAD
# define CLICK_ATOMIC_X86 1
# endif
# if (CLICK_LINUXMODULE && defined(CONFIG_SMP)) || HAVE_MULTITHREAD
# define CLICK_ATOMIC_LOCK "lock ; "
# else
# define CLICK_ATOMIC_LOCK /* nothing */
# endif
#endif
/** @file <click/atomic.hh>
* @brief An atomic 32-bit integer.
*/
/** @class atomic_uint32_t
* @brief A 32-bit integer with support for atomic operations.
*
* The atomic_uint32_t class represents a 32-bit integer, with support for
* atomic operations. The +=, -=, &=, |=, ++, and -- operations are
* implemented using atomic instructions. There are also atomic swap(),
* fetch_and_add(), dec_and_test(), and compare_swap() operations.
*
* Because of some issues with compiler implementations, atomic_uint32_t has
* no explicit constructor; to set an atomic_uint32_t to a value, use
* operator=.
*
* The atomic_uint32_t only provides true atomic semantics when that has been
* implemented. It has been implemented in the Linux kernel, and at userlevel
* (when --enable-multithread has been defined) for x86 machines. In other
* situations, it's not truly atomic (because it doesn't need to be).
*/
class atomic_uint32_t { public:
// No constructors because, unfortunately, GCC generates worse code. Use
// operator= instead.
inline uint32_t value() const;
inline operator uint32_t() const;
inline atomic_uint32_t &operator=(uint32_t x);
inline atomic_uint32_t &operator+=(int32_t delta);
inline atomic_uint32_t &operator-=(int32_t delta);
inline atomic_uint32_t &operator|=(uint32_t mask);
inline atomic_uint32_t &operator&=(uint32_t mask);
inline void operator++();
inline void operator++(int);
inline void operator--();
inline void operator--(int);
inline uint32_t swap(uint32_t desired);
inline uint32_t fetch_and_add(uint32_t delta);
inline bool dec_and_test();
inline uint32_t compare_swap(uint32_t expected, uint32_t desired);
inline bool compare_and_swap(uint32_t expected, uint32_t desired) CLICK_DEPRECATED;
inline static uint32_t swap(volatile uint32_t &x, uint32_t desired);
inline static void inc(volatile uint32_t &x);
inline static bool dec_and_test(volatile uint32_t &x);
inline static uint32_t compare_swap(volatile uint32_t &x, uint32_t expected, uint32_t desired);
inline static bool compare_and_swap(volatile uint32_t &x, uint32_t expected, uint32_t desired) CLICK_DEPRECATED;
private:
#if CLICK_LINUXMODULE
atomic_t _val;
#elif HAVE_MULTITHREAD
volatile uint32_t _val;
#else
uint32_t _val;
#endif
};
/** @brief Return the value. */
inline uint32_t
atomic_uint32_t::value() const
{
#if CLICK_LINUXMODULE
return atomic_read(&_val);
#else
return CLICK_ATOMIC_VAL;
#endif
}
/** @brief Return the value. */
inline
atomic_uint32_t::operator uint32_t() const
{
return value();
}
/** @brief Set the value to @a x. */
inline atomic_uint32_t &
atomic_uint32_t::operator=(uint32_t x)
{
#if CLICK_LINUXMODULE
atomic_set(&_val, x);
#else
CLICK_ATOMIC_VAL = x;
#endif
return *this;
}
/** @brief Atomically add @a delta to the value. */
inline atomic_uint32_t &
atomic_uint32_t::operator+=(int32_t delta)
{
#if CLICK_LINUXMODULE
atomic_add(delta, &_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "addl %1,%0"
: "=m" (CLICK_ATOMIC_VAL)
: "r" (delta), "m" (CLICK_ATOMIC_VAL)
: "cc");
#else
CLICK_ATOMIC_VAL += delta;
#endif
return *this;
}
/** @brief Atomically subtract @a delta from the value. */
inline atomic_uint32_t &
atomic_uint32_t::operator-=(int32_t delta)
{
#if CLICK_LINUXMODULE
atomic_sub(delta, &_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "subl %1,%0"
: "=m" (CLICK_ATOMIC_VAL)
: "r" (delta), "m" (CLICK_ATOMIC_VAL)
: "cc");
#else
CLICK_ATOMIC_VAL -= delta;
#endif
return *this;
}
/** @brief Atomically bitwise-or the value with @a mask. */
inline atomic_uint32_t &
atomic_uint32_t::operator|=(uint32_t mask)
{
#if CLICK_LINUXMODULE && HAVE_LINUX_ATOMIC_SET_MASK
atomic_set_mask(mask, &_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "orl %1,%0"
: "=m" (CLICK_ATOMIC_VAL)
: "r" (mask), "m" (CLICK_ATOMIC_VAL)
: "cc");
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::operator|="
unsigned long flags;
local_irq_save(flags);
CLICK_ATOMIC_VAL |= mask;
local_irq_restore(flags);
#else
CLICK_ATOMIC_VAL |= mask;
#endif
return *this;
}
/** @brief Atomically bitwise-and the value with @a mask. */
inline atomic_uint32_t &
atomic_uint32_t::operator&=(uint32_t mask)
{
#if CLICK_LINUXMODULE && HAVE_LINUX_ATOMIC_SET_MASK
atomic_clear_mask(~mask, &_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "andl %1,%0"
: "=m" (CLICK_ATOMIC_VAL)
: "r" (mask), "m" (CLICK_ATOMIC_VAL)
: "cc");
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::operator&="
unsigned long flags;
local_irq_save(flags);
CLICK_ATOMIC_VAL &= mask;
local_irq_restore(flags);
#else
CLICK_ATOMIC_VAL &= mask;
#endif
return *this;
}
/** @brief Atomically increment value @a x. */
inline void
atomic_uint32_t::inc(volatile uint32_t &x)
{
#if CLICK_LINUXMODULE
static_assert(sizeof(atomic_t) == sizeof(x), "atomic_t expected to take 32 bits.");
atomic_inc((atomic_t *) &x);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "incl %0"
: "=m" (x)
: "m" (x)
: "cc");
#else
x++;
#endif
}
/** @brief Atomically increment the value. */
inline void
atomic_uint32_t::operator++()
{
#if CLICK_LINUXMODULE
atomic_inc(&_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "incl %0"
: "=m" (CLICK_ATOMIC_VAL)
: "m" (CLICK_ATOMIC_VAL)
: "cc");
#else
CLICK_ATOMIC_VAL++;
#endif
}
/** @brief Atomically increment the value. */
inline void
atomic_uint32_t::operator++(int)
{
#if CLICK_LINUXMODULE
atomic_inc(&_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "incl %0"
: "=m" (CLICK_ATOMIC_VAL)
: "m" (CLICK_ATOMIC_VAL)
: "cc");
#else
CLICK_ATOMIC_VAL++;
#endif
}
/** @brief Atomically decrement the value. */
inline void
atomic_uint32_t::operator--()
{
#if CLICK_LINUXMODULE
atomic_dec(&_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "decl %0"
: "=m" (CLICK_ATOMIC_VAL)
: "m" (CLICK_ATOMIC_VAL)
: "cc");
#else
CLICK_ATOMIC_VAL--;
#endif
}
/** @brief Atomically decrement the value. */
inline void
atomic_uint32_t::operator--(int)
{
#if CLICK_LINUXMODULE
atomic_dec(&_val);
#elif CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "decl %0"
: "=m" (CLICK_ATOMIC_VAL)
: "m" (CLICK_ATOMIC_VAL)
: "cc");
#else
CLICK_ATOMIC_VAL--;
#endif
}
/** @brief Atomically assign the value to @a desired, returning the old value.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t actual = x;
* x = desired;
* return actual;
* @endcode
*
* Also acts as a memory barrier. */
inline uint32_t
atomic_uint32_t::swap(volatile uint32_t &x, uint32_t desired)
{
#if CLICK_ATOMIC_X86
asm volatile ("xchgl %0,%1"
: "=r" (desired), "=m" (x)
: "0" (desired), "m" (x)
: "memory");
return desired;
#elif CLICK_LINUXMODULE && defined(xchg)
return xchg(&x, desired);
#elif CLICK_LINUXMODULE
# error "need xchg for atomic_uint32_t::swap"
#else
uint32_t actual = x;
x = desired;
return actual;
#endif
}
/** @brief Atomically assign the value to @a desired, returning the old value.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t old_value = value();
* *this = desired;
* return old_value;
* @endcode
*
* Also acts as a memory barrier. */
inline uint32_t
atomic_uint32_t::swap(uint32_t desired)
{
#if CLICK_LINUXMODULE && defined(xchg)
return atomic_xchg(&_val, desired);
#elif CLICK_LINUXMODULE
# error "need xchg for atomic_uint32_t::swap"
#else
return swap(CLICK_ATOMIC_VAL, desired);
#endif
}
/** @brief Atomically add @a delta to the value, returning the old value.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t old_value = value();
* *this += delta;
* return old_value;
* @endcode */
inline uint32_t
atomic_uint32_t::fetch_and_add(uint32_t delta)
{
#if CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "xaddl %0,%1"
: "=r" (delta), "=m" (CLICK_ATOMIC_VAL)
: "0" (delta), "m" (CLICK_ATOMIC_VAL)
: "cc");
return delta;
#elif CLICK_LINUXMODULE && HAVE_LINUX_ATOMIC_ADD_RETURN
return atomic_add_return(&_val, delta) - delta;
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::fetch_and_add"
unsigned long flags;
local_irq_save(flags);
uint32_t old_value = value();
CLICK_ATOMIC_VAL += delta;
local_irq_restore(flags);
return old_value;
#else
uint32_t old_value = value();
CLICK_ATOMIC_VAL += delta;
return old_value;
#endif
}
/** @brief Atomically decrement @a x, returning true if the new @a x
* is 0.
*
* Behaves like this, but in one atomic step:
* @code
* --x;
* return x == 0;
* @endcode */
inline bool
atomic_uint32_t::dec_and_test(volatile uint32_t &x)
{
#if CLICK_LINUXMODULE
static_assert(sizeof(atomic_t) == sizeof(x), "atomic_t expected to take 32 bits.");
return atomic_dec_and_test((atomic_t *) &x);
#elif CLICK_ATOMIC_X86
uint8_t result;
asm volatile (CLICK_ATOMIC_LOCK "decl %0 ; sete %1"
: "=m" (x), "=qm" (result)
: "m" (x)
: "cc");
return result;
#else
return (--x == 0);
#endif
}
/** @brief Perform a compare-and-swap operation.
* @param x value
* @param expected test value
* @param desired new value
* @return The actual old value. If it equaled @a expected, @a x has been
* set to @a desired.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t actual = x;
* if (x == expected)
* x = desired;
* return actual;
* @endcode
*
* Also acts as a memory barrier. */
inline uint32_t
atomic_uint32_t::compare_swap(volatile uint32_t &x, uint32_t expected, uint32_t desired)
{
#if CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "cmpxchgl %2,%1"
: "=a" (expected), "=m" (x)
: "r" (desired), "0" (expected), "m" (x)
: "cc", "memory");
return expected;
#elif CLICK_LINUXMODULE && defined(cmpxchg)
return cmpxchg(&x, expected, desired);
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::compare_and_swap"
unsigned long flags;
local_irq_save(flags);
uint32_t actual = x;
if (actual == expected)
x = desired;
local_irq_restore(flags);
return actual;
#else
uint32_t actual = x;
if (actual == expected)
x = desired;
return actual;
#endif
}
/** @brief Perform a compare-and-swap operation.
* @param x value
* @param expected test value
* @param desired new value
* @return True if the old @a x equaled @a expected (in which case @a x
* was set to @a desired), false otherwise.
* @deprecated Use compare_swap instead.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t old_value = x;
* if (x == expected)
* x = desired;
* return old_value == expected;
* @endcode
*
* Also acts as a memory barrier. */
inline bool
atomic_uint32_t::compare_and_swap(volatile uint32_t &x, uint32_t expected, uint32_t desired)
{
#if CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "cmpxchgl %2,%0 ; sete %%al"
: "=m" (x), "=a" (expected)
: "r" (desired), "m" (x), "a" (expected)
: "cc", "memory");
return (uint8_t) expected;
#elif CLICK_LINUXMODULE && defined(cmpxchg)
return cmpxchg(&x, expected, desired) == expected;
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::compare_and_swap"
unsigned long flags;
local_irq_save(flags);
uint32_t old_value = x;
if (old_value == expected)
x = desired;
local_irq_restore(flags);
return old_value == expected;
#else
uint32_t old_value = x;
if (old_value == expected)
x = desired;
return old_value == expected;
#endif
}
/** @brief Atomically decrement the value, returning true if the new value
* is 0.
*
* Behaves like this, but in one atomic step:
* @code
* --*this;
* return value() == 0;
* @endcode */
inline bool
atomic_uint32_t::dec_and_test()
{
#if CLICK_LINUXMODULE
return atomic_dec_and_test(&_val);
#elif CLICK_ATOMIC_X86
uint8_t result;
asm volatile (CLICK_ATOMIC_LOCK "decl %0 ; sete %1"
: "=m" (CLICK_ATOMIC_VAL), "=qm" (result)
: "m" (CLICK_ATOMIC_VAL)
: "cc");
return result;
#else
return (--CLICK_ATOMIC_VAL == 0);
#endif
}
/** @brief Perform a compare-and-swap operation.
* @param expected test value
* @param desired new value
* @return The actual old value. If @a expected is returned, the
* value has been set to @a desired.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t actual = value();
* if (actual == expected)
* *this = desired;
* return actual;
* @endcode
*
* Also acts as a memory barrier. */
inline uint32_t
atomic_uint32_t::compare_swap(uint32_t expected, uint32_t desired)
{
#if CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "cmpxchgl %2,%1"
: "=a" (expected), "=m" (CLICK_ATOMIC_VAL)
: "r" (desired), "0" (expected), "m" (CLICK_ATOMIC_VAL)
: "cc", "memory");
return expected;
#elif CLICK_LINUXMODULE && HAVE_LINUX_ATOMIC_CMPXCHG
return atomic_cmpxchg(&_val, expected, desired);
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::compare_swap"
unsigned long flags;
local_irq_save(flags);
uint32_t actual = value();
if (actual == expected)
CLICK_ATOMIC_VAL = desired;
local_irq_restore(flags);
return actual;
#else
uint32_t actual = value();
if (actual == expected)
CLICK_ATOMIC_VAL = desired;
return actual;
#endif
}
/** @brief Perform a compare-and-swap operation.
* @param expected test value
* @param desired new value
* @return True if the old value equaled @a expected (in which case the
* value was set to @a desired), false otherwise.
* @deprecated Use compare_swap instead.
*
* Behaves like this, but in one atomic step:
* @code
* uint32_t old_value = value();
* if (old_value == expected)
* *this = desired;
* return old_value == expected;
* @endcode
*
* Also acts as a memory barrier. */
inline bool
atomic_uint32_t::compare_and_swap(uint32_t expected, uint32_t desired)
{
#if CLICK_ATOMIC_X86
asm volatile (CLICK_ATOMIC_LOCK "cmpxchgl %2,%0 ; sete %%al"
: "=m" (CLICK_ATOMIC_VAL), "=a" (expected)
: "r" (desired), "m" (CLICK_ATOMIC_VAL), "a" (expected)
: "cc", "memory");
return (uint8_t) expected;
#elif CLICK_LINUXMODULE && HAVE_LINUX_ATOMIC_CMPXCHG
return atomic_cmpxchg(&_val, expected, desired) == expected;
#elif CLICK_LINUXMODULE
# warning "using nonatomic approximation for atomic_uint32_t::compare_and_swap"
unsigned long flags;
local_irq_save(flags);
uint32_t old_value = value();
if (old_value == expected)
CLICK_ATOMIC_VAL = desired;
local_irq_restore(flags);
return old_value == expected;
#else
uint32_t old_value = value();
if (old_value == expected)
CLICK_ATOMIC_VAL = desired;
return old_value == expected;
#endif
}
inline uint32_t
operator+(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() + b.value();
}
inline uint32_t
operator-(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() - b.value();
}
inline bool
operator==(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() == b.value();
}
inline bool
operator!=(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() != b.value();
}
inline bool
operator>(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() > b.value();
}
inline bool
operator<(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() < b.value();
}
inline bool
operator>=(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() >= b.value();
}
inline bool
operator<=(const atomic_uint32_t &a, const atomic_uint32_t &b)
{
return a.value() <= b.value();
}
typedef atomic_uint32_t uatomic32_t;
CLICK_ENDDECLS
#endif