[glib: 1/3] gatomic: Add Compare and Exchange functions that returns the previous value




commit bfdeb37f6e8d9c11bf889e42e01ae7317718c49e
Author: Marco Trevisan (Treviño) <mail 3v1n0 net>
Date:   Wed Jun 22 11:24:44 2022 +0200

    gatomic: Add Compare and Exchange functions that returns the previous value
    
    Atomic primitives allow to do conditional compare and exchange but also
    to get the value that was previously stored in the atomic variable.
    
    Now, we provided an exchange function that allows to do an exchange if
    the atomic value matches an expected value but we had no way to know
    at the same time what was the value in the atomic at the moment of the
    exchange try, an this can be useful in case that the operation fails,
    for example if the current value is still acceptable for us, allowing
    to do a kind of "OR" check:
    
      gint old_value;
      gint valid_value = 222;
      while (!g_atomic_pointer_compare_and_exchange_value (&atomic,
                                                           valid_value, 555,
                                                           &old_value)
        {
          if (old_value == 555 || old_value == 222)
            valid_value = old_value;
        }

 docs/reference/glib/glib-sections.txt |   2 +
 glib/gatomic.c                        | 132 ++++++++++++++++++++++++++++++++++
 glib/gatomic.h                        |  52 ++++++++++++++
 glib/tests/atomic.c                   |  97 ++++++++++++++++++++++++-
 glib/tests/cxx.cpp                    |  40 +++++++++++
 5 files changed, 322 insertions(+), 1 deletion(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index c1abf1aee0..8dcc251a46 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -1273,6 +1273,7 @@ g_atomic_int_set
 g_atomic_int_inc
 g_atomic_int_dec_and_test
 g_atomic_int_compare_and_exchange
+g_atomic_int_compare_and_exchange_full
 g_atomic_int_exchange
 g_atomic_int_add
 g_atomic_int_and
@@ -1283,6 +1284,7 @@ g_atomic_int_xor
 g_atomic_pointer_get
 g_atomic_pointer_set
 g_atomic_pointer_compare_and_exchange
+g_atomic_pointer_compare_and_exchange_full
 g_atomic_pointer_exchange
 g_atomic_pointer_add
 g_atomic_pointer_and
diff --git a/glib/gatomic.c b/glib/gatomic.c
index 4771d449de..499fd336f3 100644
--- a/glib/gatomic.c
+++ b/glib/gatomic.c
@@ -218,6 +218,39 @@ gboolean
   return g_atomic_int_compare_and_exchange (atomic, oldval, newval);
 }
 
+/**
+ * g_atomic_int_compare_and_exchange_full:
+ * @atomic: a pointer to a #gint or #guint
+ * @oldval: the value to compare with
+ * @newval: the value to conditionally replace with
+ * @preval: (out): the contents of @atomic before this operation
+ *
+ * Compares @atomic to @oldval and, if equal, sets it to @newval.
+ * If @atomic was not equal to @oldval then no change occurs.
+ * In any case the value of @atomic before this operation is stored in @preval.
+ *
+ * This compare and exchange is done atomically.
+ *
+ * Think of this operation as an atomic version of
+ * `{ *preval = *atomic; if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * See also g_atomic_int_compare_and_exchange()
+ *
+ * Returns: %TRUE if the exchange took place
+ *
+ * Since: 2.74
+ **/
+gboolean
+(g_atomic_int_compare_and_exchange_full) (gint *atomic,
+                                          gint  oldval,
+                                          gint  newval,
+                                          gint *preval)
+{
+  return g_atomic_int_compare_and_exchange_full (atomic, oldval, newval, preval);
+}
+
 /**
  * g_atomic_int_exchange:
  * @atomic: a pointer to a #gint or #guint
@@ -430,6 +463,41 @@ gboolean
                                                 oldval, newval);
 }
 
+ /**
+ * g_atomic_pointer_compare_and_exchange_full:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @oldval: the value to compare with
+ * @newval: the value to conditionally replace with
+ * @preval: (not nullable) (out): the contents of @atomic before this operation
+ *
+ * Compares @atomic to @oldval and, if equal, sets it to @newval.
+ * If @atomic was not equal to @oldval then no change occurs.
+ * In any case the value of @atomic before this operation is stored in @preval.
+ *
+ * This compare and exchange is done atomically.
+ *
+ * Think of this operation as an atomic version of
+ * `{ *preval = *atomic; if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * See also g_atomic_pointer_compare_and_exchange()
+ *
+ * Returns: %TRUE if the exchange took place
+ *
+ * Since: 2.74
+ **/
+gboolean
+(g_atomic_pointer_compare_and_exchange_full) (void     *atomic,
+                                              gpointer  oldval,
+                                              gpointer  newval,
+                                              void     *preval)
+{
+  return g_atomic_pointer_compare_and_exchange_full ((gpointer *) atomic,
+                                                     oldval, newval,
+                                                     (gpointer *) preval);
+}
+
 /**
  * g_atomic_pointer_exchange:
  * @atomic: a pointer to a #gint or #guint
@@ -659,6 +727,16 @@ gboolean
   return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
 }
 
+gboolean
+(g_atomic_int_compare_and_exchange_full) (gint *atomic,
+                                          gint  oldval,
+                                          gint  newval,
+                                          gint *preval)
+{
+  *preval = InterlockedCompareExchange (atomic, newval, oldval);
+  return *preval == oldval;
+}
+
 gint
 (g_atomic_int_exchange) (gint *atomic,
                          gint  newval)
@@ -722,6 +800,19 @@ gboolean
   return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval;
 }
 
+gboolean
+(g_atomic_pointer_compare_and_exchange_full) (void     *atomic,
+                                              gpointer  oldval,
+                                              gpointer  newval,
+                                              void     *preval)
+{
+  gpointer *pre = preval;
+
+  *pre = InterlockedCompareExchangePointer (atomic, newval, oldval);
+
+  return *pre == oldval;
+}
+
 gpointer
 (g_atomic_pointer_exchange) (void     *atomic,
                              gpointer  newval)
@@ -851,6 +942,26 @@ gboolean
   return success;
 }
 
+gboolean
+(g_atomic_int_compare_and_exchange_full) (gint *atomic,
+                                          gint  oldval,
+                                          gint  newval,
+                                          gint *preval)
+{
+  gboolean success;
+
+  pthread_mutex_lock (&g_atomic_lock);
+
+  *preval = *atomic;
+
+  if ((success = (*atomic == oldval)))
+    *atomic = newval;
+
+  pthread_mutex_unlock (&g_atomic_lock);
+
+  return success;
+}
+
 gint
 (g_atomic_int_exchange) (gint *atomic,
                          gint  newval)
@@ -965,6 +1076,27 @@ gboolean
   return success;
 }
 
+gboolean
+(g_atomic_pointer_compare_and_exchange_full) (void     *atomic,
+                                              gpointer  oldval,
+                                              gpointer  newval,
+                                              void     *preval)
+{
+  gpointer *ptr = atomic;
+  gpointer *pre = preval;
+  gboolean success;
+
+  pthread_mutex_lock (&g_atomic_lock);
+
+  *pre = *ptr;
+  if ((success = (*ptr == oldval)))
+    *ptr = newval;
+
+  pthread_mutex_unlock (&g_atomic_lock);
+
+  return success;
+}
+
 gpointer
 (g_atomic_pointer_exchange) (void    *atomic,
                              gpointer newval)
diff --git a/glib/gatomic.h b/glib/gatomic.h
index bbdcbd5960..6a4930cddc 100644
--- a/glib/gatomic.h
+++ b/glib/gatomic.h
@@ -45,6 +45,11 @@ gboolean                g_atomic_int_compare_and_exchange     (volatile gint  *a
                                                                gint            oldval,
                                                                gint            newval);
 GLIB_AVAILABLE_IN_2_74
+gboolean                g_atomic_int_compare_and_exchange_full (gint         *atomic,
+                                                                gint          oldval,
+                                                                gint          newval,
+                                                                gint         *preval);
+GLIB_AVAILABLE_IN_2_74
 gint                    g_atomic_int_exchange                 (gint           *atomic,
                                                                gint            newval);
 GLIB_AVAILABLE_IN_ALL
@@ -70,6 +75,11 @@ gboolean                g_atomic_pointer_compare_and_exchange (volatile void  *a
                                                                gpointer        oldval,
                                                                gpointer        newval);
 GLIB_AVAILABLE_IN_2_74
+gboolean                g_atomic_pointer_compare_and_exchange_full (void     *atomic,
+                                                                    gpointer  oldval,
+                                                                    gpointer  newval,
+                                                                    void     *preval);
+GLIB_AVAILABLE_IN_2_74
 gpointer                g_atomic_pointer_exchange             (void           *atomic,
                                                                gpointer        newval);
 GLIB_AVAILABLE_IN_ALL
@@ -179,6 +189,16 @@ G_END_DECLS
     __atomic_compare_exchange_n ((atomic), (void *) (&(gaicae_oldval)), (newval), FALSE, __ATOMIC_SEQ_CST, 
__ATOMIC_SEQ_CST) ? TRUE : FALSE; \
   }))
 #endif /* defined(glib_typeof) */
+#define g_atomic_int_compare_and_exchange_full(atomic, oldval, newval, preval) \
+  (G_GNUC_EXTENSION ({                                                         \
+    G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint));                       \
+    G_STATIC_ASSERT (sizeof *(preval) == sizeof (gint));                       \
+    (void) (0 ? *(atomic) ^ (newval) ^ (oldval) ^ *(preval) : 1);              \
+    *(preval) = (oldval);                                                      \
+    __atomic_compare_exchange_n ((atomic), (preval), (newval), FALSE,          \
+                                 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)           \
+                                 ? TRUE : FALSE;                               \
+  }))
 #define g_atomic_int_exchange(atomic, newval) \
   (G_GNUC_EXTENSION ({                                                       \
     G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint));                     \
@@ -237,6 +257,17 @@ G_END_DECLS
     __atomic_compare_exchange_n ((atomic), (void *) (&(gapcae_oldval)), (newval), FALSE, __ATOMIC_SEQ_CST, 
__ATOMIC_SEQ_CST) ? TRUE : FALSE; \
   }))
 #endif /* defined(glib_typeof) */
+#define g_atomic_pointer_compare_and_exchange_full(atomic, oldval, newval, preval) \
+  (G_GNUC_EXTENSION ({                                                             \
+    G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer));                       \
+    G_STATIC_ASSERT (sizeof *(preval) == sizeof (gpointer));                       \
+    (void) (0 ? (gpointer) *(atomic) : NULL);                                      \
+    (void) (0 ? (gpointer) *(preval) : NULL);                                      \
+    *(preval) = (oldval);                                                          \
+    __atomic_compare_exchange_n ((atomic), (preval), (newval), FALSE,              \
+                                 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ?             \
+                                 TRUE : FALSE;                                     \
+  }))
 #define g_atomic_pointer_exchange(atomic, newval) \
   (G_GNUC_EXTENSION ({                                                       \
     G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer));                 \
@@ -373,6 +404,14 @@ G_END_DECLS
     (void) (0 ? *(atomic) ^ (newval) ^ (oldval) : 1);                        \
     __sync_bool_compare_and_swap ((atomic), (oldval), (newval)) ? TRUE : FALSE; \
   }))
+#define g_atomic_int_compare_and_exchange_full(atomic, oldval, newval, preval) \
+  (G_GNUC_EXTENSION ({                                                         \
+    G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint));                       \
+    G_STATIC_ASSERT (sizeof *(preval) == sizeof (gint));                       \
+    (void) (0 ? *(atomic) ^ (newval) ^ (oldval) ^ *(preval) : 1);              \
+    *(preval) = __sync_val_compare_and_swap ((atomic), (oldval), (newval));    \
+    (*(preval) == (oldval)) ? TRUE : FALSE;                                    \
+  }))
 #if defined(__GCC_HAVE_SYNC_SWAP)
 #define g_atomic_int_exchange(atomic, newval) \
   (G_GNUC_EXTENSION ({                                                       \
@@ -424,6 +463,15 @@ G_END_DECLS
     (void) (0 ? (gpointer) *(atomic) : NULL);                                \
     __sync_bool_compare_and_swap ((atomic), (oldval), (newval)) ? TRUE : FALSE; \
   }))
+#define g_atomic_pointer_compare_and_exchange_full(atomic, oldval, newval, preval) \
+  (G_GNUC_EXTENSION ({                                                             \
+    G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer));                       \
+    G_STATIC_ASSERT (sizeof *(preval) == sizeof (gpointer));                       \
+    (void) (0 ? (gpointer) *(atomic) : NULL);                                      \
+    (void) (0 ? (gpointer) *(preval) : NULL);                                      \
+    *(preval) = __sync_val_compare_and_swap ((atomic), (oldval), (newval));        \
+    (*(preval) == (oldval)) ? TRUE : FALSE;                                        \
+  }))
 #if defined(__GCC_HAVE_SYNC_SWAP)
 #define g_atomic_pointer_exchange(atomic, newval) \
   (G_GNUC_EXTENSION ({                                                       \
@@ -483,6 +531,8 @@ G_END_DECLS
   (g_atomic_int_set ((gint *) (atomic), (gint) (newval)))
 #define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
   (g_atomic_int_compare_and_exchange ((gint *) (atomic), (oldval), (newval)))
+#define g_atomic_int_compare_and_exchange_full(atomic, oldval, newval, preval) \
+  (g_atomic_int_compare_and_exchange_full ((gint *) (atomic), (oldval), (newval), (gint *) (preval)))
 #define g_atomic_int_exchange(atomic, newval) \
   (g_atomic_int_exchange ((gint *) (atomic), (newval)))
 #define g_atomic_int_add(atomic, val) \
@@ -518,6 +568,8 @@ G_END_DECLS
 
 #define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
   (g_atomic_pointer_compare_and_exchange ((atomic), (gpointer) (oldval), (gpointer) (newval)))
+#define g_atomic_pointer_compare_and_exchange_full(atomic, oldval, newval, prevval) \
+  (g_atomic_pointer_compare_and_exchange_full ((atomic), (gpointer) (oldval), (gpointer) (newval), 
(prevval)))
 #define g_atomic_pointer_exchange(atomic, newval) \
   (g_atomic_pointer_exchange ((atomic), (gpointer) (newval)))
 #define g_atomic_pointer_add(atomic, val) \
diff --git a/glib/tests/atomic.c b/glib/tests/atomic.c
index 6a595f2889..3061032b00 100644
--- a/glib/tests/atomic.c
+++ b/glib/tests/atomic.c
@@ -23,10 +23,11 @@ test_types (void)
   const gint * const *cspp;
   guint u, u2;
   gint s, s2;
-  gpointer vp, vp2;
+  gpointer vp, vp2, cp;
   const char *vp_str, *vp_str2;
   const char *volatile vp_str_vol;
   const char *str = "Hello";
+  const char *old_str;
   int *ip, *ip2;
   gsize gs, gs2;
   gboolean res;
@@ -40,6 +41,10 @@ test_types (void)
   res = g_atomic_int_compare_and_exchange (&u, 6, 7);
   g_assert_false (res);
   g_assert_cmpint (u, ==, 5);
+  res = g_atomic_int_compare_and_exchange_full (&u, 6, 7, &u2);
+  g_assert_false (res);
+  g_assert_cmpint (u, ==, 5);
+  g_assert_cmpint (u2, ==, 5);
   g_atomic_int_add (&u, 1);
   g_assert_cmpint (u, ==, 6);
   g_atomic_int_inc (&u);
@@ -66,6 +71,11 @@ test_types (void)
   res = g_atomic_int_compare_and_exchange (&s, 6, 7);
   g_assert_false (res);
   g_assert_cmpint (s, ==, 5);
+  s2 = 0;
+  res = g_atomic_int_compare_and_exchange_full (&s, 6, 7, &s2);
+  g_assert_false (res);
+  g_assert_cmpint (s, ==, 5);
+  g_assert_cmpint (s2, ==, 5);
   g_atomic_int_add (&s, 1);
   g_assert_cmpint (s, ==, 6);
   g_atomic_int_inc (&s);
@@ -91,18 +101,33 @@ test_types (void)
   g_assert_true (vp2 == 0);
   res = g_atomic_pointer_compare_and_exchange (&vp, &s, &s);
   g_assert_false (res);
+  cp = &s;
+  res = g_atomic_pointer_compare_and_exchange_full (&vp, &s, &s, &cp);
+  g_assert_false (res);
+  g_assert_null (cp);
   g_assert_true (vp == 0);
   res = g_atomic_pointer_compare_and_exchange (&vp, NULL, NULL);
   g_assert_true (res);
   g_assert_true (vp == 0);
   g_assert_null (g_atomic_pointer_exchange (&vp, &s));
   g_assert_true (vp == &s);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp, &s, NULL, &cp);
+  g_assert_true (res);
+  g_assert_true (cp == &s);
 
   g_atomic_pointer_set (&vp_str, NULL);
   res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, str);
   g_assert_true (res);
   g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
   g_assert_null (vp_str);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp_str, NULL, str, &cp);
+  g_assert_true (res);
+  g_assert_cmpstr (vp_str, ==, str);
+  g_assert_null (cp);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp_str, (char *) str, NULL, &cp);
+  g_assert_true (res);
+  g_assert_null (vp_str);
+  g_assert_true (cp == str);
 
   /* Note that atomic variables should almost certainly not be marked as
    * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
@@ -110,8 +135,15 @@ test_types (void)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
   g_atomic_pointer_set (&vp_str_vol, NULL);
+  g_atomic_pointer_set (&vp_str, str);
   res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, str);
   g_assert_true (res);
+  g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
+  g_assert_null (vp_str);
+
+  res = g_atomic_pointer_compare_and_exchange_full (&vp_str_vol, str, NULL, &old_str);
+  g_assert_true (res);
+  g_assert_true (old_str == str);
 #pragma GCC diagnostic pop
 
   g_atomic_pointer_set (&ip, 0);
@@ -121,6 +153,16 @@ test_types (void)
   g_assert_true (res);
   g_assert_true (ip == 0);
 
+  res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, 1, &cp);
+  g_assert_true (res);
+  g_assert_cmpint ((gsize) ip, ==, 1);
+  g_assert_cmpuint ((gsize) cp, ==, 0);
+
+  res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, NULL, &cp);
+  g_assert_false (res);
+  g_assert_cmpuint ((gsize) ip, ==, 1);
+  g_assert_cmpuint ((gsize) cp, ==, 1);
+
   g_atomic_pointer_set (&gs, 0);
   vp2 = (gpointer) g_atomic_pointer_get (&gs);
   gs2 = (gsize) vp2;
@@ -128,6 +170,10 @@ test_types (void)
   res = g_atomic_pointer_compare_and_exchange (&gs, NULL, (gsize) NULL);
   g_assert_true (res);
   g_assert_cmpuint (gs, ==, 0);
+  res = g_atomic_pointer_compare_and_exchange_full (&gs, NULL, (gsize) NULL, &cp);
+  g_assert_true (res);
+  g_assert_cmpuint (gs, ==, 0);
+  g_assert_cmpuint ((gsize) cp, ==, 0);
   gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
   g_assert_cmpuint (gs2, ==, 0);
   g_assert_cmpuint (gs, ==, 5);
@@ -151,6 +197,7 @@ test_types (void)
 #undef g_atomic_int_set
 #undef g_atomic_int_get
 #undef g_atomic_int_compare_and_exchange
+#undef g_atomic_int_compare_and_exchange_full
 #undef g_atomic_int_exchange
 #undef g_atomic_int_add
 #undef g_atomic_int_inc
@@ -161,6 +208,7 @@ test_types (void)
 #undef g_atomic_pointer_set
 #undef g_atomic_pointer_get
 #undef g_atomic_pointer_compare_and_exchange
+#undef g_atomic_pointer_compare_and_exchange_full
 #undef g_atomic_pointer_exchange
 #undef g_atomic_pointer_add
 #undef g_atomic_pointer_and
@@ -173,6 +221,11 @@ test_types (void)
   res = g_atomic_int_compare_and_exchange ((gint*)&u, 6, 7);
   g_assert_false (res);
   g_assert_cmpint (u, ==, 5);
+  u2 = 0;
+  res = g_atomic_int_compare_and_exchange_full ((gint*)&u, 6, 7, &u2);
+  g_assert_false (res);
+  g_assert_cmpuint (u, ==, 5);
+  g_assert_cmpuint (u2, ==, 5);
   g_atomic_int_add ((gint*)&u, 1);
   g_assert_cmpint (u, ==, 6);
   g_atomic_int_inc ((gint*)&u);
@@ -198,6 +251,11 @@ test_types (void)
   res = g_atomic_int_compare_and_exchange (&s, 6, 7);
   g_assert_false (res);
   g_assert_cmpint (s, ==, 5);
+  s2 = 0;
+  res = g_atomic_int_compare_and_exchange_full (&s, 6, 7, &s2);
+  g_assert_false (res);
+  g_assert_cmpint (s, ==, 5);
+  g_assert_cmpint (s2, ==, 5);
   g_atomic_int_add (&s, 1);
   g_assert_cmpint (s, ==, 6);
   g_atomic_int_inc (&s);
@@ -229,25 +287,48 @@ G_GNUC_END_IGNORE_DEPRECATIONS
   res = g_atomic_pointer_compare_and_exchange (&vp, &s, &s);
   g_assert_false (res);
   g_assert_true (vp == 0);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp, &s, &s, &cp);
+  g_assert_false (res);
+  g_assert_null (vp);
+  g_assert_null (cp);
   res = g_atomic_pointer_compare_and_exchange (&vp, NULL, NULL);
   g_assert_true (res);
   g_assert_true (vp == 0);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp, NULL, NULL, &cp);
+  g_assert_true (res);
+  g_assert_null (vp);
+  g_assert_null (cp);
   g_assert_null (g_atomic_pointer_exchange (&vp, &s));
   g_assert_true (vp == &s);
 
   g_atomic_pointer_set (&vp_str, NULL);
   res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, (char *) str);
   g_assert_true (res);
+  g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
+  g_assert_null (vp_str);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp_str, NULL, (char *) str, &cp);
+  g_assert_true (res);
+  g_assert_cmpstr (vp_str, ==, str);
+  g_assert_null (cp);
+  res = g_atomic_pointer_compare_and_exchange_full (&vp_str, (char *) str, NULL, &cp);
+  g_assert_true (res);
+  g_assert_null (vp_str);
+  g_assert_true (cp == str);
 
   /* Note that atomic variables should almost certainly not be marked as
    * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
    * to make sure that we don’t warn when built against older third party code. */
   g_atomic_pointer_set (&vp_str_vol, NULL);
+  g_atomic_pointer_set (&vp_str, (char *) str);
   res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, (char *) str);
   g_assert_true (res);
   g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
   g_assert_null (vp_str);
 
+  res = g_atomic_pointer_compare_and_exchange_full ((char **) &vp_str_vol, (char *) str, NULL, &old_str);
+  g_assert_true (res);
+  g_assert_true (old_str == str);
+
   g_atomic_pointer_set (&ip, 0);
   ip2 = g_atomic_pointer_get (&ip);
   g_assert_true (ip2 == 0);
@@ -255,6 +336,16 @@ G_GNUC_END_IGNORE_DEPRECATIONS
   g_assert_true (res);
   g_assert_true (ip == 0);
 
+  res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, (gpointer) 1, &cp);
+  g_assert_true (res);
+  g_assert_cmpint ((gsize) ip, ==, 1);
+  g_assert_cmpuint ((gsize) cp, ==, 0);
+
+  res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, NULL, &cp);
+  g_assert_false (res);
+  g_assert_cmpuint ((gsize) ip, ==, 1);
+  g_assert_cmpuint ((gsize) cp, ==, 1);
+
   g_atomic_pointer_set (&gs, 0);
   vp = g_atomic_pointer_get (&gs);
   gs2 = (gsize) vp;
@@ -262,6 +353,10 @@ G_GNUC_END_IGNORE_DEPRECATIONS
   res = g_atomic_pointer_compare_and_exchange (&gs, NULL, NULL);
   g_assert_true (res);
   g_assert_cmpuint (gs, ==, 0);
+  res = g_atomic_pointer_compare_and_exchange_full (&gs, NULL, NULL, &cp);
+  g_assert_true (res);
+  g_assert_cmpuint (gs, ==, 0);
+  g_assert_cmpuint ((gsize) cp, ==, 0);
   gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
   g_assert_cmpuint (gs2, ==, 0);
   g_assert_cmpuint (gs, ==, 5);
diff --git a/glib/tests/cxx.cpp b/glib/tests/cxx.cpp
index c074e18f3e..363700d14f 100644
--- a/glib/tests/cxx.cpp
+++ b/glib/tests/cxx.cpp
@@ -71,6 +71,26 @@ test_atomic_pointer_compare_and_exchange (void)
 #endif
 }
 
+static void
+test_atomic_pointer_compare_and_exchange_full (void)
+{
+#if __cplusplus >= 201103L
+  const gchar *str1 = "str1";
+  const gchar *str2 = "str2";
+  const gchar *atomic_string = str1;
+  const gchar *old;
+
+  g_test_message ("Test that g_atomic_pointer_compare_and_exchange_full() with a "
+                  "non-void* pointer doesn’t have any compiler warnings in C++ mode");
+
+  g_assert_true (g_atomic_pointer_compare_and_exchange_full (&atomic_string, str1, str2, &old));
+  g_assert_true (atomic_string == str2);
+  g_assert_true (old == str1);
+#else
+  g_test_skip ("This test requires a C++11 compiler");
+#endif
+}
+
 static void
 test_atomic_int_compare_and_exchange (void)
 {
@@ -87,6 +107,24 @@ test_atomic_int_compare_and_exchange (void)
 #endif
 }
 
+static void
+test_atomic_int_compare_and_exchange_full (void)
+{
+#if __cplusplus >= 201103L
+  gint atomic_int = 5;
+  gint old_value;
+
+  g_test_message ("Test that g_atomic_int_compare_and_exchange_full() doesn’t have "
+                  "any compiler warnings in C++ mode");
+
+  g_assert_true (g_atomic_int_compare_and_exchange_full (&atomic_int, 5, 50, &old_value));
+  g_assert_cmpint (atomic_int, ==, 50);
+  g_assert_cmpint (old_value, ==, 5);
+#else
+  g_test_skip ("This test requires a C++11 compiler");
+#endif
+}
+
 static void
 test_atomic_pointer_exchange (void)
 {
@@ -131,7 +169,9 @@ main (int argc, char *argv[])
 
   g_test_add_func ("/C++/typeof", test_typeof);
   g_test_add_func ("/C++/atomic-pointer-compare-and-exchange", test_atomic_pointer_compare_and_exchange);
+  g_test_add_func ("/C++/atomic-pointer-compare-and-exchange-full", 
test_atomic_pointer_compare_and_exchange_full);
   g_test_add_func ("/C++/atomic-int-compare-and-exchange", test_atomic_int_compare_and_exchange);
+  g_test_add_func ("/C++/atomic-int-compare-and-exchange-full", test_atomic_int_compare_and_exchange_full);
   g_test_add_func ("/C++/atomic-pointer-exchange", test_atomic_pointer_exchange);
   g_test_add_func ("/C++/atomic-int-exchange", test_atomic_int_exchange);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]