From bfdeb37f6e8d9c11bf889e42e01ae7317718c49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 22 Jun 2022 11:24:44 +0200 Subject: [PATCH 1/2] 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 c1abf1aee..8dcc251a4 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 4771d449d..499fd336f 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 bbdcbd596..6a4930cdd 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 6a595f288..3061032b0 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 c074e18f3..363700d14 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); From 922bc1d995aaa3f28a879412297d8b95f5954a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 22 Jun 2022 22:54:03 +0200 Subject: [PATCH 2/2] build: Use _GLIB prefix for GCC features we detected at build time We used a _GCC prefix that is not our namesapce, so even if such value is not provided by the compiler, let's just use a better name. --- glib/gatomic.h | 10 +++++----- meson.build | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/glib/gatomic.h b/glib/gatomic.h index 6a4930cdd..11d319edc 100644 --- a/glib/gatomic.h +++ b/glib/gatomic.h @@ -412,14 +412,14 @@ G_END_DECLS *(preval) = __sync_val_compare_and_swap ((atomic), (oldval), (newval)); \ (*(preval) == (oldval)) ? TRUE : FALSE; \ })) -#if defined(__GCC_HAVE_SYNC_SWAP) +#if defined(_GLIB_GCC_HAVE_SYNC_SWAP) #define g_atomic_int_exchange(atomic, newval) \ (G_GNUC_EXTENSION ({ \ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \ (void) (0 ? *(atomic) ^ (newval) : 1); \ (gint) __sync_swap ((atomic), (newval)); \ })) -#else /* defined(__GCC_HAVE_SYNC_SWAP) */ +#else /* defined(_GLIB_GCC_HAVE_SYNC_SWAP) */ #define g_atomic_int_exchange(atomic, newval) \ (G_GNUC_EXTENSION ({ \ gint oldval; \ @@ -431,7 +431,7 @@ G_END_DECLS } while (!__sync_bool_compare_and_swap (atomic, oldval, newval)); \ oldval; \ })) -#endif /* defined(__GCC_HAVE_SYNC_SWAP) */ +#endif /* defined(_GLIB_GCC_HAVE_SYNC_SWAP) */ #define g_atomic_int_add(atomic, val) \ (G_GNUC_EXTENSION ({ \ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \ @@ -472,7 +472,7 @@ G_END_DECLS *(preval) = __sync_val_compare_and_swap ((atomic), (oldval), (newval)); \ (*(preval) == (oldval)) ? TRUE : FALSE; \ })) -#if defined(__GCC_HAVE_SYNC_SWAP) +#if defined(_GLIB_GCC_HAVE_SYNC_SWAP) #define g_atomic_pointer_exchange(atomic, newval) \ (G_GNUC_EXTENSION ({ \ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \ @@ -491,7 +491,7 @@ G_END_DECLS } while (!__sync_bool_compare_and_swap (atomic, oldval, newval)); \ oldval; \ })) -#endif /* defined(__GCC_HAVE_SYNC_SWAP) */ +#endif /* defined(_GLIB_GCC_HAVE_SYNC_SWAP) */ #define g_atomic_pointer_add(atomic, val) \ (G_GNUC_EXTENSION ({ \ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \ diff --git a/meson.build b/meson.build index d886e4e48..4147d3a03 100644 --- a/meson.build +++ b/meson.build @@ -1874,7 +1874,7 @@ if cc.get_id() == 'msvc' or cc.get_id() == 'clang-cl' or cc.links(atomictest, na } ''' - glib_conf.set('__GCC_HAVE_SYNC_SWAP', cc.links(sync_swap_test, name : 'sync swap')) + glib_conf.set('_GLIB_GCC_HAVE_SYNC_SWAP', cc.links(sync_swap_test, name : 'sync swap')) endif else have_atomic_lock_free = false