mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-10 12:55:48 +01:00
`volatile` should not be used to indicate atomic variables, and we shouldn’t encourage its use. Keep the tests, since they check that we don’t emit warnings when built against incorrect old code which uses `volatile`. But add a comment to stop copy/paste use of `volatile` in the future. Signed-off-by: Philip Withnall <pwithnall@endlessos.org> Helps: #600
312 lines
9.0 KiB
C
312 lines
9.0 KiB
C
/*
|
||
* Copyright 2011 Red Hat, Inc.
|
||
*
|
||
* 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;
|
||
const char *vp_str;
|
||
const char *volatile vp_str_vol;
|
||
const char *str = "Hello";
|
||
int *ip, *ip2;
|
||
gsize gs, gs2;
|
||
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);
|
||
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);
|
||
|
||
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);
|
||
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);
|
||
|
||
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 (&vp, NULL, NULL);
|
||
g_assert_true (res);
|
||
g_assert_true (vp == 0);
|
||
|
||
g_atomic_pointer_set (&vp_str, NULL);
|
||
res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, str);
|
||
g_assert_true (res);
|
||
|
||
/* 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);
|
||
res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, str);
|
||
g_assert_true (res);
|
||
|
||
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);
|
||
|
||
g_atomic_pointer_set (&gs, 0);
|
||
vp2 = (gpointer) g_atomic_pointer_get (&gs);
|
||
gs2 = (gsize) vp2;
|
||
g_assert_cmpuint (gs2, ==, 0);
|
||
res = g_atomic_pointer_compare_and_exchange (&gs, NULL, NULL);
|
||
g_assert_true (res);
|
||
g_assert_cmpuint (gs, ==, 0);
|
||
gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
|
||
g_assert_cmpuint (gs2, ==, 0);
|
||
g_assert_cmpuint (gs, ==, 5);
|
||
gs2 = g_atomic_pointer_and (&gs, 6);
|
||
g_assert_cmpuint (gs2, ==, 5);
|
||
g_assert_cmpuint (gs, ==, 4);
|
||
gs2 = g_atomic_pointer_or (&gs, 8);
|
||
g_assert_cmpuint (gs2, ==, 4);
|
||
g_assert_cmpuint (gs, ==, 12);
|
||
gs2 = g_atomic_pointer_xor (&gs, 4);
|
||
g_assert_cmpuint (gs2, ==, 12);
|
||
g_assert_cmpuint (gs, ==, 8);
|
||
|
||
g_assert_cmpint (g_atomic_int_get (csp), ==, s);
|
||
g_assert_true (g_atomic_pointer_get (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_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_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);
|
||
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);
|
||
|
||
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);
|
||
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);
|
||
|
||
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 (&vp, NULL, NULL);
|
||
g_assert_true (res);
|
||
g_assert_true (vp == 0);
|
||
|
||
g_atomic_pointer_set (&vp_str, NULL);
|
||
res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, (char *) str);
|
||
g_assert_true (res);
|
||
|
||
/* 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);
|
||
res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, (char *) str);
|
||
g_assert_true (res);
|
||
|
||
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);
|
||
|
||
g_atomic_pointer_set (&gs, 0);
|
||
vp = g_atomic_pointer_get (&gs);
|
||
gs2 = (gsize) vp;
|
||
g_assert_cmpuint (gs2, ==, 0);
|
||
res = g_atomic_pointer_compare_and_exchange (&gs, NULL, NULL);
|
||
g_assert_true (res);
|
||
g_assert_cmpuint (gs, ==, 0);
|
||
gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
|
||
g_assert_cmpuint (gs2, ==, 0);
|
||
g_assert_cmpuint (gs, ==, 5);
|
||
gs2 = g_atomic_pointer_and (&gs, 6);
|
||
g_assert_cmpuint (gs2, ==, 5);
|
||
g_assert_cmpuint (gs, ==, 4);
|
||
gs2 = g_atomic_pointer_or (&gs, 8);
|
||
g_assert_cmpuint (gs2, ==, 4);
|
||
g_assert_cmpuint (gs, ==, 12);
|
||
gs2 = g_atomic_pointer_xor (&gs, 4);
|
||
g_assert_cmpuint (gs2, ==, 12);
|
||
g_assert_cmpuint (gs, ==, 8);
|
||
|
||
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 ();
|
||
}
|