glib/glib/tests/atomic.c
Alex Richardson c762d51134 gatomic: Use g(u)intptr where appropriate
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
2023-10-10 08:30:46 -07:00

441 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 dont 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 dont 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 ();
}