mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-12 07:26:15 +01:00
c762d51134
This is required for CHERI systems such as Arm Morello, where gsize is not large enough to hold a pointer. For all other architectures this should not result in any functional changes since gsize is already the same size (and I believe for most architectures even the same type) as guintptr. This is fully ABI compatible with all currently supported architectures and should also be fully API compatible except for some rather unlikely examples such as someone happening to use something like `decltype(g_atomic_pointer_add(&a, 0))` in a C++ context where it changes name mangling of some function. Helps: https://gitlab.gnome.org/GNOME/glib/-/issues/2842
441 lines
14 KiB
C
441 lines
14 KiB
C
/*
|
||
* Copyright 2011 Red Hat, Inc.
|
||
*
|
||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2.1 of the License, or (at your option) any later version.
|
||
*
|
||
* See the included COPYING file for more information.
|
||
*/
|
||
|
||
#include <glib.h>
|
||
|
||
/* We want the g_atomic_pointer_get() macros to work when compiling third party
|
||
* projects with -Wbad-function-cast.
|
||
* See https://gitlab.gnome.org/GNOME/glib/issues/1041. */
|
||
#pragma GCC diagnostic error "-Wbad-function-cast"
|
||
|
||
static void
|
||
test_types (void)
|
||
{
|
||
const gint *csp;
|
||
const gint * const *cspp;
|
||
guint u, u2;
|
||
gint s, s2;
|
||
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;
|
||
guintptr gu, gu2;
|
||
gboolean res;
|
||
|
||
csp = &s;
|
||
cspp = &csp;
|
||
|
||
g_atomic_int_set (&u, 5);
|
||
u2 = (guint) g_atomic_int_get (&u);
|
||
g_assert_cmpint (u2, ==, 5);
|
||
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);
|
||
g_assert_cmpint (u, ==, 7);
|
||
res = g_atomic_int_dec_and_test (&u);
|
||
g_assert_false (res);
|
||
g_assert_cmpint (u, ==, 6);
|
||
u2 = g_atomic_int_and (&u, 5);
|
||
g_assert_cmpint (u2, ==, 6);
|
||
g_assert_cmpint (u, ==, 4);
|
||
u2 = g_atomic_int_or (&u, 8);
|
||
g_assert_cmpint (u2, ==, 4);
|
||
g_assert_cmpint (u, ==, 12);
|
||
u2 = g_atomic_int_xor (&u, 4);
|
||
g_assert_cmpint (u2, ==, 12);
|
||
g_assert_cmpint (u, ==, 8);
|
||
u2 = g_atomic_int_exchange (&u, 55);
|
||
g_assert_cmpint (u2, ==, 8);
|
||
g_assert_cmpint (u, ==, 55);
|
||
|
||
g_atomic_int_set (&s, 5);
|
||
s2 = g_atomic_int_get (&s);
|
||
g_assert_cmpint (s2, ==, 5);
|
||
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);
|
||
g_assert_cmpint (s, ==, 7);
|
||
res = g_atomic_int_dec_and_test (&s);
|
||
g_assert_false (res);
|
||
g_assert_cmpint (s, ==, 6);
|
||
s2 = (gint) g_atomic_int_and (&s, 5);
|
||
g_assert_cmpint (s2, ==, 6);
|
||
g_assert_cmpint (s, ==, 4);
|
||
s2 = (gint) g_atomic_int_or (&s, 8);
|
||
g_assert_cmpint (s2, ==, 4);
|
||
g_assert_cmpint (s, ==, 12);
|
||
s2 = (gint) g_atomic_int_xor (&s, 4);
|
||
g_assert_cmpint (s2, ==, 12);
|
||
g_assert_cmpint (s, ==, 8);
|
||
s2 = g_atomic_int_exchange (&s, 55);
|
||
g_assert_cmpint (s2, ==, 8);
|
||
g_assert_cmpint (s, ==, 55);
|
||
|
||
g_atomic_pointer_set (&vp, 0);
|
||
vp2 = g_atomic_pointer_get (&vp);
|
||
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, &vp_str2);
|
||
g_assert_true (res);
|
||
g_assert_cmpstr (vp_str, ==, str);
|
||
g_assert_null (vp_str2);
|
||
res = g_atomic_pointer_compare_and_exchange_full (&vp_str, (char *) str, NULL, &vp_str2);
|
||
g_assert_true (res);
|
||
g_assert_null (vp_str);
|
||
g_assert_true (vp_str2 == 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. */
|
||
#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);
|
||
ip2 = g_atomic_pointer_get (&ip);
|
||
g_assert_true (ip2 == 0);
|
||
res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
|
||
g_assert_true (res);
|
||
g_assert_true (ip == 0);
|
||
|
||
res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, &s, &ip2);
|
||
g_assert_true (res);
|
||
g_assert_true (ip == &s);
|
||
g_assert_cmpuint ((guintptr) ip2, ==, 0);
|
||
|
||
res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, NULL, &ip2);
|
||
g_assert_false (res);
|
||
g_assert_true (ip == &s);
|
||
g_assert_true (ip2 == &s);
|
||
|
||
g_atomic_pointer_set (&gu, 0);
|
||
vp2 = (gpointer) g_atomic_pointer_get (&gu);
|
||
gu2 = (guintptr) vp2;
|
||
g_assert_cmpuint (gu2, ==, 0);
|
||
res = g_atomic_pointer_compare_and_exchange (&gu, NULL, (guintptr) NULL);
|
||
g_assert_true (res);
|
||
g_assert_cmpuint (gu, ==, 0);
|
||
res = g_atomic_pointer_compare_and_exchange_full (&gu, (guintptr) NULL, (guintptr) NULL, &gu2);
|
||
g_assert_true (res);
|
||
g_assert_cmpuint (gu, ==, 0);
|
||
g_assert_cmpuint (gu2, ==, 0);
|
||
gu2 = (guintptr) g_atomic_pointer_add (&gu, 5);
|
||
g_assert_cmpuint (gu2, ==, 0);
|
||
g_assert_cmpuint (gu, ==, 5);
|
||
gu2 = g_atomic_pointer_and (&gu, 6);
|
||
g_assert_cmpuint (gu2, ==, 5);
|
||
g_assert_cmpuint (gu, ==, 4);
|
||
gu2 = g_atomic_pointer_or (&gu, 8);
|
||
g_assert_cmpuint (gu2, ==, 4);
|
||
g_assert_cmpuint (gu, ==, 12);
|
||
gu2 = g_atomic_pointer_xor (&gu, 4);
|
||
g_assert_cmpuint (gu2, ==, 12);
|
||
g_assert_cmpuint (gu, ==, 8);
|
||
vp_str2 = g_atomic_pointer_exchange (&vp_str, str);
|
||
g_assert_cmpstr (vp_str, ==, str);
|
||
g_assert_null (vp_str2);
|
||
|
||
g_assert_cmpint (g_atomic_int_get (csp), ==, s);
|
||
g_assert_true (g_atomic_pointer_get ((const gint **) cspp) == csp);
|
||
|
||
/* repeat, without the macros */
|
||
#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
|
||
#undef g_atomic_int_and
|
||
#undef g_atomic_int_or
|
||
#undef g_atomic_int_xor
|
||
#undef g_atomic_int_dec_and_test
|
||
#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
|
||
#undef g_atomic_pointer_or
|
||
#undef g_atomic_pointer_xor
|
||
|
||
g_atomic_int_set ((gint*)&u, 5);
|
||
u2 = (guint) g_atomic_int_get ((gint*)&u);
|
||
g_assert_cmpint (u2, ==, 5);
|
||
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, (gint*) &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);
|
||
g_assert_cmpint (u, ==, 7);
|
||
res = g_atomic_int_dec_and_test ((gint*)&u);
|
||
g_assert_false (res);
|
||
g_assert_cmpint (u, ==, 6);
|
||
u2 = g_atomic_int_and (&u, 5);
|
||
g_assert_cmpint (u2, ==, 6);
|
||
g_assert_cmpint (u, ==, 4);
|
||
u2 = g_atomic_int_or (&u, 8);
|
||
g_assert_cmpint (u2, ==, 4);
|
||
g_assert_cmpint (u, ==, 12);
|
||
u2 = g_atomic_int_xor (&u, 4);
|
||
g_assert_cmpint (u2, ==, 12);
|
||
u2 = g_atomic_int_exchange ((gint*) &u, 55);
|
||
g_assert_cmpint (u2, ==, 8);
|
||
g_assert_cmpint (u, ==, 55);
|
||
|
||
g_atomic_int_set (&s, 5);
|
||
s2 = g_atomic_int_get (&s);
|
||
g_assert_cmpint (s2, ==, 5);
|
||
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);
|
||
g_assert_cmpint (s, ==, 7);
|
||
res = g_atomic_int_dec_and_test (&s);
|
||
g_assert_false (res);
|
||
g_assert_cmpint (s, ==, 6);
|
||
s2 = (gint) g_atomic_int_and ((guint*)&s, 5);
|
||
g_assert_cmpint (s2, ==, 6);
|
||
g_assert_cmpint (s, ==, 4);
|
||
s2 = (gint) g_atomic_int_or ((guint*)&s, 8);
|
||
g_assert_cmpint (s2, ==, 4);
|
||
g_assert_cmpint (s, ==, 12);
|
||
s2 = (gint) g_atomic_int_xor ((guint*)&s, 4);
|
||
g_assert_cmpint (s2, ==, 12);
|
||
g_assert_cmpint (s, ==, 8);
|
||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||
s2 = g_atomic_int_exchange_and_add ((gint*)&s, 1);
|
||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||
g_assert_cmpint (s2, ==, 8);
|
||
g_assert_cmpint (s, ==, 9);
|
||
s2 = g_atomic_int_exchange (&s, 55);
|
||
g_assert_cmpint (s2, ==, 9);
|
||
g_assert_cmpint (s, ==, 55);
|
||
|
||
g_atomic_pointer_set (&vp, 0);
|
||
vp2 = g_atomic_pointer_get (&vp);
|
||
g_assert_true (vp2 == 0);
|
||
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);
|
||
res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
|
||
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 ((guintptr) ip, ==, 1);
|
||
g_assert_cmpuint ((guintptr) cp, ==, 0);
|
||
|
||
res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, NULL, &cp);
|
||
g_assert_false (res);
|
||
g_assert_cmpuint ((guintptr) ip, ==, 1);
|
||
g_assert_cmpuint ((guintptr) cp, ==, 1);
|
||
|
||
g_atomic_pointer_set (&gu, 0);
|
||
vp = g_atomic_pointer_get (&gu);
|
||
gu2 = (guintptr) vp;
|
||
g_assert_cmpuint (gu2, ==, 0);
|
||
res = g_atomic_pointer_compare_and_exchange (&gu, NULL, NULL);
|
||
g_assert_true (res);
|
||
g_assert_cmpuint (gu, ==, 0);
|
||
res = g_atomic_pointer_compare_and_exchange_full (&gu, NULL, NULL, &cp);
|
||
g_assert_true (res);
|
||
g_assert_cmpuint (gu, ==, 0);
|
||
g_assert_cmpuint ((guintptr) cp, ==, 0);
|
||
gu2 = (guintptr) g_atomic_pointer_add (&gu, 5);
|
||
g_assert_cmpuint (gu2, ==, 0);
|
||
g_assert_cmpuint (gu, ==, 5);
|
||
gu2 = g_atomic_pointer_and (&gu, 6);
|
||
g_assert_cmpuint (gu2, ==, 5);
|
||
g_assert_cmpuint (gu, ==, 4);
|
||
gu2 = g_atomic_pointer_or (&gu, 8);
|
||
g_assert_cmpuint (gu2, ==, 4);
|
||
g_assert_cmpuint (gu, ==, 12);
|
||
gu2 = g_atomic_pointer_xor (&gu, 4);
|
||
g_assert_cmpuint (gu2, ==, 12);
|
||
g_assert_cmpuint (gu, ==, 8);
|
||
vp2 = g_atomic_pointer_exchange (&gu, NULL);
|
||
gu2 = (guintptr) vp2;
|
||
g_assert_cmpuint (gu2, ==, 8);
|
||
g_assert_null ((gpointer) gu);
|
||
|
||
g_assert_cmpint (g_atomic_int_get (csp), ==, s);
|
||
g_assert_true (g_atomic_pointer_get (cspp) == csp);
|
||
}
|
||
|
||
#define THREADS 10
|
||
#define ROUNDS 10000
|
||
|
||
gint bucket[THREADS]; /* never contested by threads, not accessed atomically */
|
||
gint atomic; /* (atomic) */
|
||
|
||
static gpointer
|
||
thread_func (gpointer data)
|
||
{
|
||
gint idx = GPOINTER_TO_INT (data);
|
||
gint i;
|
||
gint d;
|
||
|
||
for (i = 0; i < ROUNDS; i++)
|
||
{
|
||
d = g_random_int_range (-10, 100);
|
||
bucket[idx] += d;
|
||
g_atomic_int_add (&atomic, d);
|
||
g_thread_yield ();
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static void
|
||
test_threaded (void)
|
||
{
|
||
gint sum;
|
||
gint i;
|
||
GThread *threads[THREADS];
|
||
|
||
atomic = 0;
|
||
for (i = 0; i < THREADS; i++)
|
||
bucket[i] = 0;
|
||
|
||
for (i = 0; i < THREADS; i++)
|
||
threads[i] = g_thread_new ("atomic", thread_func, GINT_TO_POINTER (i));
|
||
|
||
for (i = 0; i < THREADS; i++)
|
||
g_thread_join (threads[i]);
|
||
|
||
sum = 0;
|
||
for (i = 0; i < THREADS; i++)
|
||
sum += bucket[i];
|
||
|
||
g_assert_cmpint (sum, ==, atomic);
|
||
}
|
||
|
||
int
|
||
main (int argc, char **argv)
|
||
{
|
||
g_test_init (&argc, &argv, NULL);
|
||
|
||
g_test_add_func ("/atomic/types", test_types);
|
||
g_test_add_func ("/atomic/threaded", test_threaded);
|
||
|
||
return g_test_run ();
|
||
}
|