1/*
2 * Copyright 2011 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * See the included COPYING file for more information.
10 */
11
12#include <glib.h>
13
14/* We want the g_atomic_pointer_get() macros to work when compiling third party
15 * projects with -Wbad-function-cast.
16 * See https://gitlab.gnome.org/GNOME/glib/issues/1041. */
17#pragma GCC diagnostic error "-Wbad-function-cast"
18
19static void
20test_types (void)
21{
22 const gint *csp;
23 const gint * const *cspp;
24 guint u, u2;
25 gint s, s2;
26 gpointer vp, vp2;
27 const char *vp_str;
28 const char *volatile vp_str_vol;
29 const char *str = "Hello";
30 int *ip, *ip2;
31 gsize gs, gs2;
32 gboolean res;
33
34 csp = &s;
35 cspp = &csp;
36
37 g_atomic_int_set (&u, 5);
38 u2 = (guint) g_atomic_int_get (&u);
39 g_assert_cmpint (u2, ==, 5);
40 res = g_atomic_int_compare_and_exchange (&u, 6, 7);
41 g_assert_false (res);
42 g_assert_cmpint (u, ==, 5);
43 g_atomic_int_add (&u, 1);
44 g_assert_cmpint (u, ==, 6);
45 g_atomic_int_inc (&u);
46 g_assert_cmpint (u, ==, 7);
47 res = g_atomic_int_dec_and_test (&u);
48 g_assert_false (res);
49 g_assert_cmpint (u, ==, 6);
50 u2 = g_atomic_int_and (&u, 5);
51 g_assert_cmpint (u2, ==, 6);
52 g_assert_cmpint (u, ==, 4);
53 u2 = g_atomic_int_or (&u, 8);
54 g_assert_cmpint (u2, ==, 4);
55 g_assert_cmpint (u, ==, 12);
56 u2 = g_atomic_int_xor (&u, 4);
57 g_assert_cmpint (u2, ==, 12);
58 g_assert_cmpint (u, ==, 8);
59
60 g_atomic_int_set (&s, 5);
61 s2 = g_atomic_int_get (&s);
62 g_assert_cmpint (s2, ==, 5);
63 res = g_atomic_int_compare_and_exchange (&s, 6, 7);
64 g_assert_false (res);
65 g_assert_cmpint (s, ==, 5);
66 g_atomic_int_add (&s, 1);
67 g_assert_cmpint (s, ==, 6);
68 g_atomic_int_inc (&s);
69 g_assert_cmpint (s, ==, 7);
70 res = g_atomic_int_dec_and_test (&s);
71 g_assert_false (res);
72 g_assert_cmpint (s, ==, 6);
73 s2 = (gint) g_atomic_int_and (&s, 5);
74 g_assert_cmpint (s2, ==, 6);
75 g_assert_cmpint (s, ==, 4);
76 s2 = (gint) g_atomic_int_or (&s, 8);
77 g_assert_cmpint (s2, ==, 4);
78 g_assert_cmpint (s, ==, 12);
79 s2 = (gint) g_atomic_int_xor (&s, 4);
80 g_assert_cmpint (s2, ==, 12);
81 g_assert_cmpint (s, ==, 8);
82
83 g_atomic_pointer_set (&vp, 0);
84 vp2 = g_atomic_pointer_get (&vp);
85 g_assert_true (vp2 == 0);
86 res = g_atomic_pointer_compare_and_exchange (&vp, &s, &s);
87 g_assert_false (res);
88 g_assert_true (vp == 0);
89 res = g_atomic_pointer_compare_and_exchange (&vp, NULL, NULL);
90 g_assert_true (res);
91 g_assert_true (vp == 0);
92
93 g_atomic_pointer_set (&vp_str, NULL);
94 res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, str);
95 g_assert_true (res);
96
97 /* Note that atomic variables should almost certainly not be marked as
98 * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
99 * to make sure that we don’t warn when built against older third party code. */
100 g_atomic_pointer_set (&vp_str_vol, NULL);
101 res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, str);
102 g_assert_true (res);
103
104 g_atomic_pointer_set (&ip, 0);
105 ip2 = g_atomic_pointer_get (&ip);
106 g_assert_true (ip2 == 0);
107 res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
108 g_assert_true (res);
109 g_assert_true (ip == 0);
110
111 g_atomic_pointer_set (&gs, 0);
112 vp2 = (gpointer) g_atomic_pointer_get (&gs);
113 gs2 = (gsize) vp2;
114 g_assert_cmpuint (gs2, ==, 0);
115 res = g_atomic_pointer_compare_and_exchange (&gs, NULL, NULL);
116 g_assert_true (res);
117 g_assert_cmpuint (gs, ==, 0);
118 gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
119 g_assert_cmpuint (gs2, ==, 0);
120 g_assert_cmpuint (gs, ==, 5);
121 gs2 = g_atomic_pointer_and (&gs, 6);
122 g_assert_cmpuint (gs2, ==, 5);
123 g_assert_cmpuint (gs, ==, 4);
124 gs2 = g_atomic_pointer_or (&gs, 8);
125 g_assert_cmpuint (gs2, ==, 4);
126 g_assert_cmpuint (gs, ==, 12);
127 gs2 = g_atomic_pointer_xor (&gs, 4);
128 g_assert_cmpuint (gs2, ==, 12);
129 g_assert_cmpuint (gs, ==, 8);
130
131 g_assert_cmpint (g_atomic_int_get (csp), ==, s);
132 g_assert_true (g_atomic_pointer_get (cspp) == csp);
133
134 /* repeat, without the macros */
135#undef g_atomic_int_set
136#undef g_atomic_int_get
137#undef g_atomic_int_compare_and_exchange
138#undef g_atomic_int_add
139#undef g_atomic_int_inc
140#undef g_atomic_int_and
141#undef g_atomic_int_or
142#undef g_atomic_int_xor
143#undef g_atomic_int_dec_and_test
144#undef g_atomic_pointer_set
145#undef g_atomic_pointer_get
146#undef g_atomic_pointer_compare_and_exchange
147#undef g_atomic_pointer_add
148#undef g_atomic_pointer_and
149#undef g_atomic_pointer_or
150#undef g_atomic_pointer_xor
151
152 g_atomic_int_set (atomic: (gint*)&u, newval: 5);
153 u2 = (guint) g_atomic_int_get (atomic: (gint*)&u);
154 g_assert_cmpint (u2, ==, 5);
155 res = g_atomic_int_compare_and_exchange (atomic: (gint*)&u, oldval: 6, newval: 7);
156 g_assert_false (res);
157 g_assert_cmpint (u, ==, 5);
158 g_atomic_int_add (atomic: (gint*)&u, val: 1);
159 g_assert_cmpint (u, ==, 6);
160 g_atomic_int_inc (atomic: (gint*)&u);
161 g_assert_cmpint (u, ==, 7);
162 res = g_atomic_int_dec_and_test (atomic: (gint*)&u);
163 g_assert_false (res);
164 g_assert_cmpint (u, ==, 6);
165 u2 = g_atomic_int_and (atomic: &u, val: 5);
166 g_assert_cmpint (u2, ==, 6);
167 g_assert_cmpint (u, ==, 4);
168 u2 = g_atomic_int_or (atomic: &u, val: 8);
169 g_assert_cmpint (u2, ==, 4);
170 g_assert_cmpint (u, ==, 12);
171 u2 = g_atomic_int_xor (atomic: &u, val: 4);
172 g_assert_cmpint (u2, ==, 12);
173
174 g_atomic_int_set (atomic: &s, newval: 5);
175 s2 = g_atomic_int_get (atomic: &s);
176 g_assert_cmpint (s2, ==, 5);
177 res = g_atomic_int_compare_and_exchange (atomic: &s, oldval: 6, newval: 7);
178 g_assert_false (res);
179 g_assert_cmpint (s, ==, 5);
180 g_atomic_int_add (atomic: &s, val: 1);
181 g_assert_cmpint (s, ==, 6);
182 g_atomic_int_inc (atomic: &s);
183 g_assert_cmpint (s, ==, 7);
184 res = g_atomic_int_dec_and_test (atomic: &s);
185 g_assert_false (res);
186 g_assert_cmpint (s, ==, 6);
187 s2 = (gint) g_atomic_int_and (atomic: (guint*)&s, val: 5);
188 g_assert_cmpint (s2, ==, 6);
189 g_assert_cmpint (s, ==, 4);
190 s2 = (gint) g_atomic_int_or (atomic: (guint*)&s, val: 8);
191 g_assert_cmpint (s2, ==, 4);
192 g_assert_cmpint (s, ==, 12);
193 s2 = (gint) g_atomic_int_xor (atomic: (guint*)&s, val: 4);
194 g_assert_cmpint (s2, ==, 12);
195 g_assert_cmpint (s, ==, 8);
196G_GNUC_BEGIN_IGNORE_DEPRECATIONS
197 s2 = g_atomic_int_exchange_and_add (atomic: (gint*)&s, val: 1);
198G_GNUC_END_IGNORE_DEPRECATIONS
199 g_assert_cmpint (s2, ==, 8);
200 g_assert_cmpint (s, ==, 9);
201
202 g_atomic_pointer_set (atomic: &vp, newval: 0);
203 vp2 = g_atomic_pointer_get (atomic: &vp);
204 g_assert_true (vp2 == 0);
205 res = g_atomic_pointer_compare_and_exchange (atomic: &vp, oldval: &s, newval: &s);
206 g_assert_false (res);
207 g_assert_true (vp == 0);
208 res = g_atomic_pointer_compare_and_exchange (atomic: &vp, NULL, NULL);
209 g_assert_true (res);
210 g_assert_true (vp == 0);
211
212 g_atomic_pointer_set (atomic: &vp_str, NULL);
213 res = g_atomic_pointer_compare_and_exchange (atomic: &vp_str, NULL, newval: (char *) str);
214 g_assert_true (res);
215
216 /* Note that atomic variables should almost certainly not be marked as
217 * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
218 * to make sure that we don’t warn when built against older third party code. */
219 g_atomic_pointer_set (atomic: &vp_str_vol, NULL);
220 res = g_atomic_pointer_compare_and_exchange (atomic: &vp_str_vol, NULL, newval: (char *) str);
221 g_assert_true (res);
222
223 g_atomic_pointer_set (atomic: &ip, newval: 0);
224 ip2 = g_atomic_pointer_get (atomic: &ip);
225 g_assert_true (ip2 == 0);
226 res = g_atomic_pointer_compare_and_exchange (atomic: &ip, NULL, NULL);
227 g_assert_true (res);
228 g_assert_true (ip == 0);
229
230 g_atomic_pointer_set (atomic: &gs, newval: 0);
231 vp = g_atomic_pointer_get (atomic: &gs);
232 gs2 = (gsize) vp;
233 g_assert_cmpuint (gs2, ==, 0);
234 res = g_atomic_pointer_compare_and_exchange (atomic: &gs, NULL, NULL);
235 g_assert_true (res);
236 g_assert_cmpuint (gs, ==, 0);
237 gs2 = (gsize) g_atomic_pointer_add (atomic: &gs, val: 5);
238 g_assert_cmpuint (gs2, ==, 0);
239 g_assert_cmpuint (gs, ==, 5);
240 gs2 = g_atomic_pointer_and (atomic: &gs, val: 6);
241 g_assert_cmpuint (gs2, ==, 5);
242 g_assert_cmpuint (gs, ==, 4);
243 gs2 = g_atomic_pointer_or (atomic: &gs, val: 8);
244 g_assert_cmpuint (gs2, ==, 4);
245 g_assert_cmpuint (gs, ==, 12);
246 gs2 = g_atomic_pointer_xor (atomic: &gs, val: 4);
247 g_assert_cmpuint (gs2, ==, 12);
248 g_assert_cmpuint (gs, ==, 8);
249
250 g_assert_cmpint (g_atomic_int_get (csp), ==, s);
251 g_assert_true (g_atomic_pointer_get (cspp) == csp);
252}
253
254#define THREADS 10
255#define ROUNDS 10000
256
257gint bucket[THREADS]; /* never contested by threads, not accessed atomically */
258gint atomic; /* (atomic) */
259
260static gpointer
261thread_func (gpointer data)
262{
263 gint idx = GPOINTER_TO_INT (data);
264 gint i;
265 gint d;
266
267 for (i = 0; i < ROUNDS; i++)
268 {
269 d = g_random_int_range (begin: -10, end: 100);
270 bucket[idx] += d;
271 g_atomic_int_add (atomic: &atomic, val: d);
272 g_thread_yield ();
273 }
274
275 return NULL;
276}
277
278static void
279test_threaded (void)
280{
281 gint sum;
282 gint i;
283 GThread *threads[THREADS];
284
285 atomic = 0;
286 for (i = 0; i < THREADS; i++)
287 bucket[i] = 0;
288
289 for (i = 0; i < THREADS; i++)
290 threads[i] = g_thread_new (name: "atomic", func: thread_func, GINT_TO_POINTER (i));
291
292 for (i = 0; i < THREADS; i++)
293 g_thread_join (thread: threads[i]);
294
295 sum = 0;
296 for (i = 0; i < THREADS; i++)
297 sum += bucket[i];
298
299 g_assert_cmpint (sum, ==, atomic);
300}
301
302int
303main (int argc, char **argv)
304{
305 g_test_init (argc: &argc, argv: &argv, NULL);
306
307 g_test_add_func (testpath: "/atomic/types", test_func: test_types);
308 g_test_add_func (testpath: "/atomic/threaded", test_func: test_threaded);
309
310 return g_test_run ();
311}
312

source code of gtk/subprojects/glib/glib/tests/atomic.c