1 /*
2 * Copyright 2011 Red Hat, Inc.
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * See the included COPYING file for more information.
12 */
13
14 #include <glib.h>
15
16 /* We want the g_atomic_pointer_get() macros to work when compiling third party
17 * projects with -Wbad-function-cast.
18 * See https://gitlab.gnome.org/GNOME/glib/issues/1041. */
19 #pragma GCC diagnostic error "-Wbad-function-cast"
20
21 static void
22 test_types (void)
23 {
24 const gint *csp;
25 const gint * const *cspp;
26 guint u, u2;
27 gint s, s2;
28 gpointer vp, vp2, cp;
29 const char *vp_str, *vp_str2;
30 const char *volatile vp_str_vol;
31 const char *str = "Hello";
32 const char *old_str;
33 int *ip, *ip2;
34 guintptr gu, gu2;
35 gboolean res;
36
37 csp = &s;
38 cspp = &csp;
39
40 g_atomic_int_set (&u, 5);
41 u2 = (guint) g_atomic_int_get (&u);
42 g_assert_cmpint (u2, ==, 5);
43 res = g_atomic_int_compare_and_exchange (&u, 6, 7);
44 g_assert_false (res);
45 g_assert_cmpint (u, ==, 5);
46 res = g_atomic_int_compare_and_exchange_full (&u, 6, 7, &u2);
47 g_assert_false (res);
48 g_assert_cmpint (u, ==, 5);
49 g_assert_cmpint (u2, ==, 5);
50 g_atomic_int_add (&u, 1);
51 g_assert_cmpint (u, ==, 6);
52 g_atomic_int_inc (&u);
53 g_assert_cmpint (u, ==, 7);
54 res = g_atomic_int_dec_and_test (&u);
55 g_assert_false (res);
56 g_assert_cmpint (u, ==, 6);
57 u2 = g_atomic_int_and (&u, 5);
58 g_assert_cmpint (u2, ==, 6);
59 g_assert_cmpint (u, ==, 4);
60 u2 = g_atomic_int_or (&u, 8);
61 g_assert_cmpint (u2, ==, 4);
62 g_assert_cmpint (u, ==, 12);
63 u2 = g_atomic_int_xor (&u, 4);
64 g_assert_cmpint (u2, ==, 12);
65 g_assert_cmpint (u, ==, 8);
66 u2 = g_atomic_int_exchange (&u, 55);
67 g_assert_cmpint (u2, ==, 8);
68 g_assert_cmpint (u, ==, 55);
69
70 g_atomic_int_set (&s, 5);
71 s2 = g_atomic_int_get (&s);
72 g_assert_cmpint (s2, ==, 5);
73 res = g_atomic_int_compare_and_exchange (&s, 6, 7);
74 g_assert_false (res);
75 g_assert_cmpint (s, ==, 5);
76 s2 = 0;
77 res = g_atomic_int_compare_and_exchange_full (&s, 6, 7, &s2);
78 g_assert_false (res);
79 g_assert_cmpint (s, ==, 5);
80 g_assert_cmpint (s2, ==, 5);
81 g_atomic_int_add (&s, 1);
82 g_assert_cmpint (s, ==, 6);
83 g_atomic_int_inc (&s);
84 g_assert_cmpint (s, ==, 7);
85 res = g_atomic_int_dec_and_test (&s);
86 g_assert_false (res);
87 g_assert_cmpint (s, ==, 6);
88 s2 = (gint) g_atomic_int_and (&s, 5);
89 g_assert_cmpint (s2, ==, 6);
90 g_assert_cmpint (s, ==, 4);
91 s2 = (gint) g_atomic_int_or (&s, 8);
92 g_assert_cmpint (s2, ==, 4);
93 g_assert_cmpint (s, ==, 12);
94 s2 = (gint) g_atomic_int_xor (&s, 4);
95 g_assert_cmpint (s2, ==, 12);
96 g_assert_cmpint (s, ==, 8);
97 s2 = g_atomic_int_exchange (&s, 55);
98 g_assert_cmpint (s2, ==, 8);
99 g_assert_cmpint (s, ==, 55);
100
101 g_atomic_pointer_set (&vp, 0);
102 vp2 = g_atomic_pointer_get (&vp);
103 g_assert_true (vp2 == 0);
104 res = g_atomic_pointer_compare_and_exchange (&vp, &s, &s);
105 g_assert_false (res);
106 cp = &s;
107 res = g_atomic_pointer_compare_and_exchange_full (&vp, &s, &s, &cp);
108 g_assert_false (res);
109 g_assert_null (cp);
110 g_assert_true (vp == 0);
111 res = g_atomic_pointer_compare_and_exchange (&vp, NULL, NULL);
112 g_assert_true (res);
113 g_assert_true (vp == 0);
114 g_assert_null (g_atomic_pointer_exchange (&vp, &s));
115 g_assert_true (vp == &s);
116 res = g_atomic_pointer_compare_and_exchange_full (&vp, &s, NULL, &cp);
117 g_assert_true (res);
118 g_assert_true (cp == &s);
119
120 g_atomic_pointer_set (&vp_str, NULL);
121 res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, str);
122 g_assert_true (res);
123 g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
124 g_assert_null (vp_str);
125 res = g_atomic_pointer_compare_and_exchange_full (&vp_str, NULL, str, &vp_str2);
126 g_assert_true (res);
127 g_assert_cmpstr (vp_str, ==, str);
128 g_assert_null (vp_str2);
129 res = g_atomic_pointer_compare_and_exchange_full (&vp_str, (char *) str, NULL, &vp_str2);
130 g_assert_true (res);
131 g_assert_null (vp_str);
132 g_assert_true (vp_str2 == str);
133
134 /* Note that atomic variables should almost certainly not be marked as
135 * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
136 * to make sure that we don’t warn when built against older third party code. */
137 #pragma GCC diagnostic push
138 #pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
139 g_atomic_pointer_set (&vp_str_vol, NULL);
140 g_atomic_pointer_set (&vp_str, str);
141 res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, str);
142 g_assert_true (res);
143 g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
144 g_assert_null (vp_str);
145
146 res = g_atomic_pointer_compare_and_exchange_full (&vp_str_vol, str, NULL, &old_str);
147 g_assert_true (res);
148 g_assert_true (old_str == str);
149 #pragma GCC diagnostic pop
150
151 g_atomic_pointer_set (&ip, 0);
152 ip2 = g_atomic_pointer_get (&ip);
153 g_assert_true (ip2 == 0);
154 res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
155 g_assert_true (res);
156 g_assert_true (ip == 0);
157
158 res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, &s, &ip2);
159 g_assert_true (res);
160 g_assert_true (ip == &s);
161 g_assert_cmpuint ((guintptr) ip2, ==, 0);
162
163 res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, NULL, &ip2);
164 g_assert_false (res);
165 g_assert_true (ip == &s);
166 g_assert_true (ip2 == &s);
167
168 g_atomic_pointer_set (&gu, 0);
169 vp2 = (gpointer) g_atomic_pointer_get (&gu);
170 gu2 = (guintptr) vp2;
171 g_assert_cmpuint (gu2, ==, 0);
172 res = g_atomic_pointer_compare_and_exchange (&gu, NULL, (guintptr) NULL);
173 g_assert_true (res);
174 g_assert_cmpuint (gu, ==, 0);
175 res = g_atomic_pointer_compare_and_exchange_full (&gu, (guintptr) NULL, (guintptr) NULL, &gu2);
176 g_assert_true (res);
177 g_assert_cmpuint (gu, ==, 0);
178 g_assert_cmpuint (gu2, ==, 0);
179 gu2 = (guintptr) g_atomic_pointer_add (&gu, 5);
180 g_assert_cmpuint (gu2, ==, 0);
181 g_assert_cmpuint (gu, ==, 5);
182 gu2 = g_atomic_pointer_and (&gu, 6);
183 g_assert_cmpuint (gu2, ==, 5);
184 g_assert_cmpuint (gu, ==, 4);
185 gu2 = g_atomic_pointer_or (&gu, 8);
186 g_assert_cmpuint (gu2, ==, 4);
187 g_assert_cmpuint (gu, ==, 12);
188 gu2 = g_atomic_pointer_xor (&gu, 4);
189 g_assert_cmpuint (gu2, ==, 12);
190 g_assert_cmpuint (gu, ==, 8);
191 vp_str2 = g_atomic_pointer_exchange (&vp_str, str);
192 g_assert_cmpstr (vp_str, ==, str);
193 g_assert_null (vp_str2);
194
195 g_assert_cmpint (g_atomic_int_get (csp), ==, s);
196 g_assert_true (g_atomic_pointer_get ((const gint **) cspp) == csp);
197
198 /* repeat, without the macros */
199 #undef g_atomic_int_set
200 #undef g_atomic_int_get
201 #undef g_atomic_int_compare_and_exchange
202 #undef g_atomic_int_compare_and_exchange_full
203 #undef g_atomic_int_exchange
204 #undef g_atomic_int_add
205 #undef g_atomic_int_inc
206 #undef g_atomic_int_and
207 #undef g_atomic_int_or
208 #undef g_atomic_int_xor
209 #undef g_atomic_int_dec_and_test
210 #undef g_atomic_pointer_set
211 #undef g_atomic_pointer_get
212 #undef g_atomic_pointer_compare_and_exchange
213 #undef g_atomic_pointer_compare_and_exchange_full
214 #undef g_atomic_pointer_exchange
215 #undef g_atomic_pointer_add
216 #undef g_atomic_pointer_and
217 #undef g_atomic_pointer_or
218 #undef g_atomic_pointer_xor
219
220 g_atomic_int_set ((gint*)&u, 5);
221 u2 = (guint) g_atomic_int_get ((gint*)&u);
222 g_assert_cmpint (u2, ==, 5);
223 res = g_atomic_int_compare_and_exchange ((gint*)&u, 6, 7);
224 g_assert_false (res);
225 g_assert_cmpint (u, ==, 5);
226 u2 = 0;
227 res = g_atomic_int_compare_and_exchange_full ((gint*)&u, 6, 7, (gint*) &u2);
228 g_assert_false (res);
229 g_assert_cmpuint (u, ==, 5);
230 g_assert_cmpuint (u2, ==, 5);
231 g_atomic_int_add ((gint*)&u, 1);
232 g_assert_cmpint (u, ==, 6);
233 g_atomic_int_inc ((gint*)&u);
234 g_assert_cmpint (u, ==, 7);
235 res = g_atomic_int_dec_and_test ((gint*)&u);
236 g_assert_false (res);
237 g_assert_cmpint (u, ==, 6);
238 u2 = g_atomic_int_and (&u, 5);
239 g_assert_cmpint (u2, ==, 6);
240 g_assert_cmpint (u, ==, 4);
241 u2 = g_atomic_int_or (&u, 8);
242 g_assert_cmpint (u2, ==, 4);
243 g_assert_cmpint (u, ==, 12);
244 u2 = g_atomic_int_xor (&u, 4);
245 g_assert_cmpint (u2, ==, 12);
246 u2 = g_atomic_int_exchange ((gint*) &u, 55);
247 g_assert_cmpint (u2, ==, 8);
248 g_assert_cmpint (u, ==, 55);
249
250 g_atomic_int_set (&s, 5);
251 s2 = g_atomic_int_get (&s);
252 g_assert_cmpint (s2, ==, 5);
253 res = g_atomic_int_compare_and_exchange (&s, 6, 7);
254 g_assert_false (res);
255 g_assert_cmpint (s, ==, 5);
256 s2 = 0;
257 res = g_atomic_int_compare_and_exchange_full (&s, 6, 7, &s2);
258 g_assert_false (res);
259 g_assert_cmpint (s, ==, 5);
260 g_assert_cmpint (s2, ==, 5);
261 g_atomic_int_add (&s, 1);
262 g_assert_cmpint (s, ==, 6);
263 g_atomic_int_inc (&s);
264 g_assert_cmpint (s, ==, 7);
265 res = g_atomic_int_dec_and_test (&s);
266 g_assert_false (res);
267 g_assert_cmpint (s, ==, 6);
268 s2 = (gint) g_atomic_int_and ((guint*)&s, 5);
269 g_assert_cmpint (s2, ==, 6);
270 g_assert_cmpint (s, ==, 4);
271 s2 = (gint) g_atomic_int_or ((guint*)&s, 8);
272 g_assert_cmpint (s2, ==, 4);
273 g_assert_cmpint (s, ==, 12);
274 s2 = (gint) g_atomic_int_xor ((guint*)&s, 4);
275 g_assert_cmpint (s2, ==, 12);
276 g_assert_cmpint (s, ==, 8);
277 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
278 s2 = g_atomic_int_exchange_and_add ((gint*)&s, 1);
279 G_GNUC_END_IGNORE_DEPRECATIONS
280 g_assert_cmpint (s2, ==, 8);
281 g_assert_cmpint (s, ==, 9);
282 s2 = g_atomic_int_exchange (&s, 55);
283 g_assert_cmpint (s2, ==, 9);
284 g_assert_cmpint (s, ==, 55);
285
286 g_atomic_pointer_set (&vp, 0);
287 vp2 = g_atomic_pointer_get (&vp);
288 g_assert_true (vp2 == 0);
289 res = g_atomic_pointer_compare_and_exchange (&vp, &s, &s);
290 g_assert_false (res);
291 g_assert_true (vp == 0);
292 res = g_atomic_pointer_compare_and_exchange_full (&vp, &s, &s, &cp);
293 g_assert_false (res);
294 g_assert_null (vp);
295 g_assert_null (cp);
296 res = g_atomic_pointer_compare_and_exchange (&vp, NULL, NULL);
297 g_assert_true (res);
298 g_assert_true (vp == 0);
299 res = g_atomic_pointer_compare_and_exchange_full (&vp, NULL, NULL, &cp);
300 g_assert_true (res);
301 g_assert_null (vp);
302 g_assert_null (cp);
303 g_assert_null (g_atomic_pointer_exchange (&vp, &s));
304 g_assert_true (vp == &s);
305
306 g_atomic_pointer_set (&vp_str, NULL);
307 res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, (char *) str);
308 g_assert_true (res);
309 g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
310 g_assert_null (vp_str);
311 res = g_atomic_pointer_compare_and_exchange_full (&vp_str, NULL, (char *) str, &cp);
312 g_assert_true (res);
313 g_assert_cmpstr (vp_str, ==, str);
314 g_assert_null (cp);
315 res = g_atomic_pointer_compare_and_exchange_full (&vp_str, (char *) str, NULL, &cp);
316 g_assert_true (res);
317 g_assert_null (vp_str);
318 g_assert_true (cp == str);
319
320 /* Note that atomic variables should almost certainly not be marked as
321 * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
322 * to make sure that we don’t warn when built against older third party code. */
323 g_atomic_pointer_set (&vp_str_vol, NULL);
324 g_atomic_pointer_set (&vp_str, (char *) str);
325 res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, (char *) str);
326 g_assert_true (res);
327 g_assert_cmpstr (g_atomic_pointer_exchange (&vp_str, NULL), ==, str);
328 g_assert_null (vp_str);
329
330 res = g_atomic_pointer_compare_and_exchange_full ((char **) &vp_str_vol, (char *) str, NULL, &old_str);
331 g_assert_true (res);
332 g_assert_true (old_str == str);
333
334 g_atomic_pointer_set (&ip, 0);
335 ip2 = g_atomic_pointer_get (&ip);
336 g_assert_true (ip2 == 0);
337 res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
338 g_assert_true (res);
339 g_assert_true (ip == 0);
340
341 res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, (gpointer) 1, &cp);
342 g_assert_true (res);
343 g_assert_cmpint ((guintptr) ip, ==, 1);
344 g_assert_cmpuint ((guintptr) cp, ==, 0);
345
346 res = g_atomic_pointer_compare_and_exchange_full (&ip, NULL, NULL, &cp);
347 g_assert_false (res);
348 g_assert_cmpuint ((guintptr) ip, ==, 1);
349 g_assert_cmpuint ((guintptr) cp, ==, 1);
350
351 g_atomic_pointer_set (&gu, 0);
352 vp = g_atomic_pointer_get (&gu);
353 gu2 = (guintptr) vp;
354 g_assert_cmpuint (gu2, ==, 0);
355 res = g_atomic_pointer_compare_and_exchange (&gu, NULL, NULL);
356 g_assert_true (res);
357 g_assert_cmpuint (gu, ==, 0);
358 res = g_atomic_pointer_compare_and_exchange_full (&gu, NULL, NULL, &cp);
359 g_assert_true (res);
360 g_assert_cmpuint (gu, ==, 0);
361 g_assert_cmpuint ((guintptr) cp, ==, 0);
362 gu2 = (guintptr) g_atomic_pointer_add (&gu, 5);
363 g_assert_cmpuint (gu2, ==, 0);
364 g_assert_cmpuint (gu, ==, 5);
365 gu2 = g_atomic_pointer_and (&gu, 6);
366 g_assert_cmpuint (gu2, ==, 5);
367 g_assert_cmpuint (gu, ==, 4);
368 gu2 = g_atomic_pointer_or (&gu, 8);
369 g_assert_cmpuint (gu2, ==, 4);
370 g_assert_cmpuint (gu, ==, 12);
371 gu2 = g_atomic_pointer_xor (&gu, 4);
372 g_assert_cmpuint (gu2, ==, 12);
373 g_assert_cmpuint (gu, ==, 8);
374 vp2 = g_atomic_pointer_exchange (&gu, NULL);
375 gu2 = (guintptr) vp2;
376 g_assert_cmpuint (gu2, ==, 8);
377 g_assert_null ((gpointer) gu);
378
379 g_assert_cmpint (g_atomic_int_get (csp), ==, s);
380 g_assert_true (g_atomic_pointer_get (cspp) == csp);
381 }
382
383 #define THREADS 10
384 #define ROUNDS 10000
385
386 gint bucket[THREADS]; /* never contested by threads, not accessed atomically */
387 gint atomic; /* (atomic) */
388
389 static gpointer
390 thread_func (gpointer data)
391 {
392 gint idx = GPOINTER_TO_INT (data);
393 gint i;
394 gint d;
395
396 for (i = 0; i < ROUNDS; i++)
397 {
398 d = g_random_int_range (-10, 100);
399 bucket[idx] += d;
400 g_atomic_int_add (&atomic, d);
401 g_thread_yield ();
402 }
403
404 return NULL;
405 }
406
407 static void
408 test_threaded (void)
409 {
410 gint sum;
411 gint i;
412 GThread *threads[THREADS];
413
414 atomic = 0;
415 for (i = 0; i < THREADS; i++)
416 bucket[i] = 0;
417
418 for (i = 0; i < THREADS; i++)
419 threads[i] = g_thread_new ("atomic", thread_func, GINT_TO_POINTER (i));
420
421 for (i = 0; i < THREADS; i++)
422 g_thread_join (threads[i]);
423
424 sum = 0;
425 for (i = 0; i < THREADS; i++)
426 sum += bucket[i];
427
428 g_assert_cmpint (sum, ==, atomic);
429 }
430
431 int
432 main (int argc, char **argv)
433 {
434 g_test_init (&argc, &argv, NULL);
435
436 g_test_add_func ("/atomic/types", test_types);
437 g_test_add_func ("/atomic/threaded", test_threaded);
438
439 return g_test_run ();
440 }