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