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 
19 static void
test_types(void)20 test_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 #pragma GCC diagnostic push
101 #pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
102   g_atomic_pointer_set (&vp_str_vol, NULL);
103   res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, str);
104   g_assert_true (res);
105 #pragma GCC diagnostic pop
106 
107   g_atomic_pointer_set (&ip, 0);
108   ip2 = g_atomic_pointer_get (&ip);
109   g_assert_true (ip2 == 0);
110   res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
111   g_assert_true (res);
112   g_assert_true (ip == 0);
113 
114   g_atomic_pointer_set (&gs, 0);
115   vp2 = (gpointer) g_atomic_pointer_get (&gs);
116   gs2 = (gsize) vp2;
117   g_assert_cmpuint (gs2, ==, 0);
118   res = g_atomic_pointer_compare_and_exchange (&gs, NULL, (gsize) NULL);
119   g_assert_true (res);
120   g_assert_cmpuint (gs, ==, 0);
121   gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
122   g_assert_cmpuint (gs2, ==, 0);
123   g_assert_cmpuint (gs, ==, 5);
124   gs2 = g_atomic_pointer_and (&gs, 6);
125   g_assert_cmpuint (gs2, ==, 5);
126   g_assert_cmpuint (gs, ==, 4);
127   gs2 = g_atomic_pointer_or (&gs, 8);
128   g_assert_cmpuint (gs2, ==, 4);
129   g_assert_cmpuint (gs, ==, 12);
130   gs2 = g_atomic_pointer_xor (&gs, 4);
131   g_assert_cmpuint (gs2, ==, 12);
132   g_assert_cmpuint (gs, ==, 8);
133 
134   g_assert_cmpint (g_atomic_int_get (csp), ==, s);
135   g_assert_true (g_atomic_pointer_get ((const gint **) cspp) == csp);
136 
137   /* repeat, without the macros */
138 #undef g_atomic_int_set
139 #undef g_atomic_int_get
140 #undef g_atomic_int_compare_and_exchange
141 #undef g_atomic_int_add
142 #undef g_atomic_int_inc
143 #undef g_atomic_int_and
144 #undef g_atomic_int_or
145 #undef g_atomic_int_xor
146 #undef g_atomic_int_dec_and_test
147 #undef g_atomic_pointer_set
148 #undef g_atomic_pointer_get
149 #undef g_atomic_pointer_compare_and_exchange
150 #undef g_atomic_pointer_add
151 #undef g_atomic_pointer_and
152 #undef g_atomic_pointer_or
153 #undef g_atomic_pointer_xor
154 
155   g_atomic_int_set ((gint*)&u, 5);
156   u2 = (guint) g_atomic_int_get ((gint*)&u);
157   g_assert_cmpint (u2, ==, 5);
158   res = g_atomic_int_compare_and_exchange ((gint*)&u, 6, 7);
159   g_assert_false (res);
160   g_assert_cmpint (u, ==, 5);
161   g_atomic_int_add ((gint*)&u, 1);
162   g_assert_cmpint (u, ==, 6);
163   g_atomic_int_inc ((gint*)&u);
164   g_assert_cmpint (u, ==, 7);
165   res = g_atomic_int_dec_and_test ((gint*)&u);
166   g_assert_false (res);
167   g_assert_cmpint (u, ==, 6);
168   u2 = g_atomic_int_and (&u, 5);
169   g_assert_cmpint (u2, ==, 6);
170   g_assert_cmpint (u, ==, 4);
171   u2 = g_atomic_int_or (&u, 8);
172   g_assert_cmpint (u2, ==, 4);
173   g_assert_cmpint (u, ==, 12);
174   u2 = g_atomic_int_xor (&u, 4);
175   g_assert_cmpint (u2, ==, 12);
176 
177   g_atomic_int_set (&s, 5);
178   s2 = g_atomic_int_get (&s);
179   g_assert_cmpint (s2, ==, 5);
180   res = g_atomic_int_compare_and_exchange (&s, 6, 7);
181   g_assert_false (res);
182   g_assert_cmpint (s, ==, 5);
183   g_atomic_int_add (&s, 1);
184   g_assert_cmpint (s, ==, 6);
185   g_atomic_int_inc (&s);
186   g_assert_cmpint (s, ==, 7);
187   res = g_atomic_int_dec_and_test (&s);
188   g_assert_false (res);
189   g_assert_cmpint (s, ==, 6);
190   s2 = (gint) g_atomic_int_and ((guint*)&s, 5);
191   g_assert_cmpint (s2, ==, 6);
192   g_assert_cmpint (s, ==, 4);
193   s2 = (gint) g_atomic_int_or ((guint*)&s, 8);
194   g_assert_cmpint (s2, ==, 4);
195   g_assert_cmpint (s, ==, 12);
196   s2 = (gint) g_atomic_int_xor ((guint*)&s, 4);
197   g_assert_cmpint (s2, ==, 12);
198   g_assert_cmpint (s, ==, 8);
199 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
200   s2 = g_atomic_int_exchange_and_add ((gint*)&s, 1);
201 G_GNUC_END_IGNORE_DEPRECATIONS
202   g_assert_cmpint (s2, ==, 8);
203   g_assert_cmpint (s, ==, 9);
204 
205   g_atomic_pointer_set (&vp, 0);
206   vp2 = g_atomic_pointer_get (&vp);
207   g_assert_true (vp2 == 0);
208   res = g_atomic_pointer_compare_and_exchange (&vp, &s, &s);
209   g_assert_false (res);
210   g_assert_true (vp == 0);
211   res = g_atomic_pointer_compare_and_exchange (&vp, NULL, NULL);
212   g_assert_true (res);
213   g_assert_true (vp == 0);
214 
215   g_atomic_pointer_set (&vp_str, NULL);
216   res = g_atomic_pointer_compare_and_exchange (&vp_str, NULL, (char *) str);
217   g_assert_true (res);
218 
219   /* Note that atomic variables should almost certainly not be marked as
220    * `volatile` — see http://isvolatileusefulwiththreads.in/c/. This test exists
221    * to make sure that we don’t warn when built against older third party code. */
222   g_atomic_pointer_set (&vp_str_vol, NULL);
223   res = g_atomic_pointer_compare_and_exchange (&vp_str_vol, NULL, (char *) str);
224   g_assert_true (res);
225 
226   g_atomic_pointer_set (&ip, 0);
227   ip2 = g_atomic_pointer_get (&ip);
228   g_assert_true (ip2 == 0);
229   res = g_atomic_pointer_compare_and_exchange (&ip, NULL, NULL);
230   g_assert_true (res);
231   g_assert_true (ip == 0);
232 
233   g_atomic_pointer_set (&gs, 0);
234   vp = g_atomic_pointer_get (&gs);
235   gs2 = (gsize) vp;
236   g_assert_cmpuint (gs2, ==, 0);
237   res = g_atomic_pointer_compare_and_exchange (&gs, NULL, NULL);
238   g_assert_true (res);
239   g_assert_cmpuint (gs, ==, 0);
240   gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
241   g_assert_cmpuint (gs2, ==, 0);
242   g_assert_cmpuint (gs, ==, 5);
243   gs2 = g_atomic_pointer_and (&gs, 6);
244   g_assert_cmpuint (gs2, ==, 5);
245   g_assert_cmpuint (gs, ==, 4);
246   gs2 = g_atomic_pointer_or (&gs, 8);
247   g_assert_cmpuint (gs2, ==, 4);
248   g_assert_cmpuint (gs, ==, 12);
249   gs2 = g_atomic_pointer_xor (&gs, 4);
250   g_assert_cmpuint (gs2, ==, 12);
251   g_assert_cmpuint (gs, ==, 8);
252 
253   g_assert_cmpint (g_atomic_int_get (csp), ==, s);
254   g_assert_true (g_atomic_pointer_get (cspp) == csp);
255 }
256 
257 #define THREADS 10
258 #define ROUNDS 10000
259 
260 gint bucket[THREADS];  /* never contested by threads, not accessed atomically */
261 gint atomic;  /* (atomic) */
262 
263 static gpointer
thread_func(gpointer data)264 thread_func (gpointer data)
265 {
266   gint idx = GPOINTER_TO_INT (data);
267   gint i;
268   gint d;
269 
270   for (i = 0; i < ROUNDS; i++)
271     {
272       d = g_random_int_range (-10, 100);
273       bucket[idx] += d;
274       g_atomic_int_add (&atomic, d);
275       g_thread_yield ();
276     }
277 
278   return NULL;
279 }
280 
281 static void
test_threaded(void)282 test_threaded (void)
283 {
284   gint sum;
285   gint i;
286   GThread *threads[THREADS];
287 
288   atomic = 0;
289   for (i = 0; i < THREADS; i++)
290     bucket[i] = 0;
291 
292   for (i = 0; i < THREADS; i++)
293     threads[i] = g_thread_new ("atomic", thread_func, GINT_TO_POINTER (i));
294 
295   for (i = 0; i < THREADS; i++)
296     g_thread_join (threads[i]);
297 
298   sum = 0;
299   for (i = 0; i < THREADS; i++)
300     sum += bucket[i];
301 
302   g_assert_cmpint (sum, ==, atomic);
303 }
304 
305 int
main(int argc,char ** argv)306 main (int argc, char **argv)
307 {
308   g_test_init (&argc, &argv, NULL);
309 
310   g_test_add_func ("/atomic/types", test_types);
311   g_test_add_func ("/atomic/threaded", test_threaded);
312 
313   return g_test_run ();
314 }
315