|
|
|
|
@ -21,219 +21,18 @@
|
|
|
|
|
* \author Victor Julien <victor@inliniac.net>
|
|
|
|
|
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
|
|
|
|
|
*
|
|
|
|
|
* API for atomic operations. Uses atomic instructions (GCC only at this time)
|
|
|
|
|
* where available, falls back to (spin)locked* operations otherwise.
|
|
|
|
|
* API for atomic operations. Uses atomic instructions (GCC only at this time).
|
|
|
|
|
*
|
|
|
|
|
* To prevent developers from accidentally working with the atomic variables
|
|
|
|
|
* directly instead of through the proper macro's, a marco trick is performed
|
|
|
|
|
* that exposes different variable names than the developer uses. So if the dev
|
|
|
|
|
* uses "somevar", internally "somevar_sc_atomic__" is used.
|
|
|
|
|
*
|
|
|
|
|
* Where available, we use __sync_fetch_and_add and
|
|
|
|
|
* __sync_bool_compare_and_swap. If those are unavailable, the API
|
|
|
|
|
* transparently created a matching (spin)lock for each atomic variable. The
|
|
|
|
|
* lock will be named "somevar_sc_lock__"
|
|
|
|
|
*
|
|
|
|
|
* (*) where spinlocks are unavailable, the threading api falls back to mutex
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef __UTIL_ATOMIC_H__
|
|
|
|
|
#define __UTIL_ATOMIC_H__
|
|
|
|
|
|
|
|
|
|
/* test if we have atomic operations support */
|
|
|
|
|
#if (!defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || \
|
|
|
|
|
!defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1))
|
|
|
|
|
|
|
|
|
|
/* Do not have atomic operations support, so implement them with locks. */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief wrapper to declare an atomic variable including a (spin) lock
|
|
|
|
|
* to protect it.
|
|
|
|
|
*
|
|
|
|
|
* \warning Variable and lock are _not_ initialized.
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_DECLARE(type, name) \
|
|
|
|
|
type name ## _sc_atomic__; \
|
|
|
|
|
SCSpinlock name ## _sc_lock__
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief wrapper to reference an atomic variable already declared on another file (including the spin lock)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_EXTERN(type, name) \
|
|
|
|
|
extern type name ## _sc_atomic__; \
|
|
|
|
|
extern SCSpinlock name ## _sc_lock__
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief wrapper to declare an atomic variable including a (spin) lock
|
|
|
|
|
* to protect it and initialize them.
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_DECL_AND_INIT(type, name) \
|
|
|
|
|
type name ## _sc_atomic__ = 0; \
|
|
|
|
|
SCSpinlock name ## _sc_lock__; \
|
|
|
|
|
SCSpinInit(&(name ## _sc_lock__), 0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Initialize the previously declared atomic variable and it's
|
|
|
|
|
* lock.
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_INIT(name) do { \
|
|
|
|
|
SCSpinInit(&(name ## _sc_lock__), 0); \
|
|
|
|
|
(name ## _sc_atomic__) = 0; \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Initialize the previously declared atomic variable and it's
|
|
|
|
|
* lock.
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_RESET(name) do { \
|
|
|
|
|
(name ## _sc_atomic__) = 0; \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Destroy the lock used to protect this variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_DESTROY(name) do { \
|
|
|
|
|
SCSpinDestroy(&(name ## _sc_lock__)); \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief add a value to our atomic variable
|
|
|
|
|
*
|
|
|
|
|
* \param name the atomic variable
|
|
|
|
|
* \param val the value to add to the variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_ADD(name, val) ({\
|
|
|
|
|
typeof(name ## _sc_atomic__) var; \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
(name ## _sc_atomic__) += (val); \
|
|
|
|
|
var = (name ## _sc_atomic__); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while(0); \
|
|
|
|
|
var ; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief sub a value from our atomic variable
|
|
|
|
|
*
|
|
|
|
|
* \param name the atomic variable
|
|
|
|
|
* \param val the value to sub from the variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_SUB(name, val) ({ \
|
|
|
|
|
typeof(name ## _sc_atomic__) var; \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
(name ## _sc_atomic__) -= (val); \
|
|
|
|
|
var = (name ## _sc_atomic__); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while(0); \
|
|
|
|
|
var ; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Bitwise AND a value from our atomic variable
|
|
|
|
|
*
|
|
|
|
|
* \param name the atomic variable
|
|
|
|
|
* \param val the value to sub from the variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_AND(name, val) \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
(name ## _sc_atomic__) &= (val); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Bitwise OR a value from our atomic variable
|
|
|
|
|
*
|
|
|
|
|
* \param name the atomic variable
|
|
|
|
|
* \param val the value to sub from the variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_OR(name, val) \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
(name ## _sc_atomic__) |= (val); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Bitwise NAND a value from our atomic variable
|
|
|
|
|
*
|
|
|
|
|
* \param name the atomic variable
|
|
|
|
|
* \param val the value to sub from the variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_NAND(name, val) \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
(name ## _sc_atomic__) = ~(name ## _sc_atomic__) & (val); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Bitwise XOR a value from our atomic variable
|
|
|
|
|
*
|
|
|
|
|
* \param name the atomic variable
|
|
|
|
|
* \param val the value to sub from the variable
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_XOR(name, val) \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
(name ## _sc_atomic__) ^= (val); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Get the value from the atomic variable.
|
|
|
|
|
*
|
|
|
|
|
* \retval var value
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_GET(name) ({ \
|
|
|
|
|
typeof(name ## _sc_atomic__) var; \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
var = (name ## _sc_atomic__); \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while (0); \
|
|
|
|
|
var; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Set the value for the atomic variable.
|
|
|
|
|
*
|
|
|
|
|
* \retval var value
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_SET(name, val) ({ \
|
|
|
|
|
typeof(name ## _sc_atomic__) var; \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock(&(name ## _sc_lock__)); \
|
|
|
|
|
var = (name ## _sc_atomic__) = val; \
|
|
|
|
|
SCSpinUnlock(&(name ## _sc_lock__)); \
|
|
|
|
|
} while (0); \
|
|
|
|
|
var; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief atomic Compare and Switch
|
|
|
|
|
*
|
|
|
|
|
* \warning "name" is passed to us as "&var"
|
|
|
|
|
*/
|
|
|
|
|
#define SC_ATOMIC_CAS(name, cmpval, newval) ({ \
|
|
|
|
|
char r = 0; \
|
|
|
|
|
do { \
|
|
|
|
|
SCSpinLock((name ## _sc_lock__)); \
|
|
|
|
|
if (*(name ## _sc_atomic__) == (cmpval)) { \
|
|
|
|
|
*(name ## _sc_atomic__) = (newval); \
|
|
|
|
|
r = 1; \
|
|
|
|
|
} \
|
|
|
|
|
SCSpinUnlock((name ## _sc_lock__)); \
|
|
|
|
|
} while(0); \
|
|
|
|
|
r; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
#else /* we do have support for CAS */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief wrapper for OS/compiler specific atomic compare and swap (CAS)
|
|
|
|
|
* function.
|
|
|
|
|
@ -288,8 +87,6 @@
|
|
|
|
|
#define SCAtomicSubAndFetch(addr, value) \
|
|
|
|
|
__sync_sub_and_fetch((addr), (value))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief wrapper for OS/compiler specific atomic fetch and "AND"
|
|
|
|
|
* function.
|
|
|
|
|
@ -320,7 +117,6 @@
|
|
|
|
|
#define SCAtomicFetchAndXor(addr, value) \
|
|
|
|
|
__sync_fetch_and_xor((addr), (value))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief wrapper for OS/compiler specific atomic fetch and or
|
|
|
|
|
* function.
|
|
|
|
|
@ -467,8 +263,6 @@
|
|
|
|
|
; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
#endif /* !no atomic operations */
|
|
|
|
|
|
|
|
|
|
void SCAtomicRegisterTests(void);
|
|
|
|
|
|
|
|
|
|
#endif /* __UTIL_ATOMIC_H__ */
|
|
|
|
|
|