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 |
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 | 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); |
196 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
197 | s2 = g_atomic_int_exchange_and_add (atomic: (gint*)&s, val: 1); |
198 | G_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 | |
257 | gint bucket[THREADS]; /* never contested by threads, not accessed atomically */ |
258 | gint atomic; /* (atomic) */ |
259 | |
260 | static gpointer |
261 | thread_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 | |
278 | static void |
279 | test_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 | |
302 | int |
303 | main (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 | |