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

Reply via email to