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