Added cas operations for 32 and 64 bit atomic variables. These use relaxed memory order (as all other operations).
Signed-off-by: Petri Savolainen <petri.savolai...@nokia.com> --- include/odp/api/atomic.h | 37 ++++++++++++++++++++++ platform/linux-generic/include/odp/atomic.h | 24 ++++++++++++++ .../linux-generic/include/odp/plat/atomic_types.h | 21 ++++++++++-- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/include/odp/api/atomic.h b/include/odp/api/atomic.h index 97e8639..957d304 100644 --- a/include/odp/api/atomic.h +++ b/include/odp/api/atomic.h @@ -136,6 +136,25 @@ uint32_t odp_atomic_fetch_dec_u32(odp_atomic_u32_t *atom); void odp_atomic_dec_u32(odp_atomic_u32_t *atom); /** + * Compare and swap atomic uint32 variable + * + * Compares value of atomic variable to the value pointed by 'old_val'. + * If values are equal, the operation writes 'new_val' into the atomic variable + * and returns success. If they are not equal, the operation writes current + * value of atomic variable into 'old_val' and returns failure. + * + * @param atom Pointer to atomic variable + * @param[in,out] old_val Pointer to the old value of the atomic variable. + * Operation updates this value on failure. + * @param new_val New value to be written into the atomic variable + * + * @return 0 on failure, !0 on success + * + */ +int odp_atomic_cas_u32(odp_atomic_u32_t *atom, uint32_t *old_val, + uint32_t new_val); + +/** * Initialize atomic uint64 variable * * @param atom Pointer to atomic variable @@ -229,6 +248,24 @@ uint64_t odp_atomic_fetch_dec_u64(odp_atomic_u64_t *atom); void odp_atomic_dec_u64(odp_atomic_u64_t *atom); /** + * Compare and swap atomic uint64 variable + * + * Compares value of atomic variable to the value pointed by 'old_val'. + * If values are equal, the operation writes 'new_val' into the atomic variable + * and returns success. If they are not equal, the operation writes current + * value of atomic variable into 'old_val' and returns failure. + * + * @param atom Pointer to atomic variable + * @param[in,out] old_val Pointer to the old value of the atomic variable. + * Operation updates this value on failure. + * @param new_val New value to be written into the atomic variable + * + * @return 0 on failure, !0 on success + */ +int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val, + uint64_t new_val); + +/** * @} */ diff --git a/platform/linux-generic/include/odp/atomic.h b/platform/linux-generic/include/odp/atomic.h index deb4039..5b13e02 100644 --- a/platform/linux-generic/include/odp/atomic.h +++ b/platform/linux-generic/include/odp/atomic.h @@ -85,6 +85,15 @@ static inline void odp_atomic_dec_u32(odp_atomic_u32_t *atom) (void)__atomic_fetch_sub(&atom->v, 1, __ATOMIC_RELAXED); } +static inline int odp_atomic_cas_u32(odp_atomic_u32_t *atom, uint32_t *old_val, + uint32_t new_val) +{ + return __atomic_compare_exchange_n(&atom->v, old_val, new_val, + 0 /* strong */, + __ATOMIC_RELAXED, + __ATOMIC_RELAXED); +} + static inline void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val) { atom->v = val; @@ -186,6 +195,21 @@ static inline void odp_atomic_dec_u64(odp_atomic_u64_t *atom) #endif } +static inline int odp_atomic_cas_u64(odp_atomic_u64_t *atom, uint64_t *old_val, + uint64_t new_val) +{ +#if __GCC_ATOMIC_LLONG_LOCK_FREE < 2 + int ret; + *old_val = ATOMIC_OP(atom, ATOMIC_CAS_OP(&ret, *old_val, new_val)); + return ret; +#else + return __atomic_compare_exchange_n(&atom->v, old_val, new_val, + 0 /* strong */, + __ATOMIC_RELAXED, + __ATOMIC_RELAXED); +#endif +} + /** * @} */ diff --git a/platform/linux-generic/include/odp/plat/atomic_types.h b/platform/linux-generic/include/odp/plat/atomic_types.h index 0f6c353..ea8fc2a 100644 --- a/platform/linux-generic/include/odp/plat/atomic_types.h +++ b/platform/linux-generic/include/odp/plat/atomic_types.h @@ -42,6 +42,21 @@ struct odp_atomic_u32_s { } ODP_ALIGNED(sizeof(uint32_t)); /* Enforce alignement! */; #if __GCC_ATOMIC_LLONG_LOCK_FREE < 2 + +/** + * @internal + * CAS operation expression for the ATOMIC_OP macro + */ +#define ATOMIC_CAS_OP(ret_ptr, old_val, new_val) \ +({ \ + if (atom->v == (old_val)) { \ + atom->v = (new_val); \ + *(ret_ptr) = 1; \ + } else { \ + *(ret_ptr) = 0; \ + } \ +}) + /** * @internal * Helper macro for lock-based atomic operations on 64-bit integers @@ -51,14 +66,14 @@ struct odp_atomic_u32_s { */ #define ATOMIC_OP(atom, expr) \ ({ \ - uint64_t old_val; \ + uint64_t _old_val; \ /* Loop while lock is already taken, stop when lock becomes clear */ \ while (__atomic_test_and_set(&(atom)->lock, __ATOMIC_ACQUIRE)) \ (void)0; \ - old_val = (atom)->v; \ + _old_val = (atom)->v; \ (expr); /* Perform whatever update is desired */ \ __atomic_clear(&(atom)->lock, __ATOMIC_RELEASE); \ - old_val; /* Return old value */ \ + _old_val; /* Return old value */ \ }) #endif -- 2.6.2 _______________________________________________ lng-odp mailing list lng-odp@lists.linaro.org https://lists.linaro.org/mailman/listinfo/lng-odp