1 /* g_once_init_*() test
2 * Copyright (C) 2007 Tim Janik
3 *
4 * This work is provided "as is"; redistribution and modification
5 * in whole or in part, in any medium, physical or electronic is
6 * permitted without restriction.
7
8 * This work is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
12 * In no event shall the authors or contributors be liable for any
13 * direct, indirect, incidental, special, exemplary, or consequential
14 * damages (including, but not limited to, procurement of substitute
15 * goods or services; loss of use, data, or profits; or business
16 * interruption) however caused and on any theory of liability, whether
17 * in contract, strict liability, or tort (including negligence or
18 * otherwise) arising in any way out of the use of this software, even
19 * if advised of the possibility of such damage.
20 */
21 #include <glib.h>
22 #include <stdlib.h>
23
24 #define N_THREADS (13)
25
26 static GMutex *tmutex = NULL;
27 static GCond *tcond = NULL;
28 static volatile int thread_call_count = 0;
29 static char dummy_value = 'x';
30
31 static void
assert_singleton_execution1(void)32 assert_singleton_execution1 (void)
33 {
34 static volatile int seen_execution = 0;
35 int old_seen_execution = g_atomic_int_exchange_and_add (&seen_execution, 1);
36 if (old_seen_execution != 0)
37 g_error ("%s: function executed more than once", G_STRFUNC);
38 }
39
40 static void
assert_singleton_execution2(void)41 assert_singleton_execution2 (void)
42 {
43 static volatile int seen_execution = 0;
44 int old_seen_execution = g_atomic_int_exchange_and_add (&seen_execution, 1);
45 if (old_seen_execution != 0)
46 g_error ("%s: function executed more than once", G_STRFUNC);
47 }
48
49 static void
assert_singleton_execution3(void)50 assert_singleton_execution3 (void)
51 {
52 static volatile int seen_execution = 0;
53 int old_seen_execution = g_atomic_int_exchange_and_add (&seen_execution, 1);
54 if (old_seen_execution != 0)
55 g_error ("%s: function executed more than once", G_STRFUNC);
56 }
57
58 static void
initializer1(void)59 initializer1 (void)
60 {
61 static volatile gsize initialized = 0;
62 if (g_once_init_enter (&initialized))
63 {
64 gsize initval = 42;
65 assert_singleton_execution1();
66 g_once_init_leave (&initialized, initval);
67 }
68 }
69
70 static gpointer
initializer2(void)71 initializer2 (void)
72 {
73 static volatile gsize initialized = 0;
74 if (g_once_init_enter (&initialized))
75 {
76 void *pointer_value = &dummy_value;
77 assert_singleton_execution2();
78 g_once_init_leave (&initialized, (gsize) pointer_value);
79 }
80 return (void*) initialized;
81 }
82
83 static void
initializer3(void)84 initializer3 (void)
85 {
86 static volatile gsize initialized = 0;
87 if (g_once_init_enter (&initialized))
88 {
89 gsize initval = 42;
90 assert_singleton_execution3();
91 g_usleep (25 * 1000); /* waste time for multiple threads to wait */
92 g_once_init_leave (&initialized, initval);
93 }
94 }
95
96 static gpointer
tmain_call_initializer3(gpointer user_data)97 tmain_call_initializer3 (gpointer user_data)
98 {
99 g_mutex_lock (tmutex);
100 g_cond_wait (tcond, tmutex);
101 g_mutex_unlock (tmutex);
102 //g_printf ("[");
103 initializer3();
104 //g_printf ("]\n");
105 g_atomic_int_exchange_and_add (&thread_call_count, 1);
106 return NULL;
107 }
108
109 static void* stress_concurrent_initializers (void*);
110
111 int
main(int argc,char * argv[])112 main (int argc,
113 char *argv[])
114 {
115 GThread *threads[N_THREADS];
116 int i;
117 /* test simple initializer */
118 initializer1();
119 initializer1();
120 /* test pointer initializer */
121 void *p = initializer2();
122 g_assert (p == &dummy_value);
123 p = initializer2();
124 g_assert (p == &dummy_value);
125 /* setup threads */
126 g_thread_init (NULL);
127 tmutex = g_mutex_new ();
128 tcond = g_cond_new ();
129 /* start multiple threads for initializer3() */
130 g_mutex_lock (tmutex);
131 for (i = 0; i < N_THREADS; i++)
132 threads[i] = g_thread_create (tmain_call_initializer3, 0, FALSE, NULL);
133 g_mutex_unlock (tmutex);
134 /* concurrently call initializer3() */
135 g_cond_broadcast (tcond);
136 /* loop until all threads passed the call to initializer3() */
137 while (g_atomic_int_get (&thread_call_count) < i)
138 {
139 if (rand() % 2)
140 g_thread_yield(); /* concurrent shuffling for single core */
141 else
142 g_usleep (1000); /* concurrent shuffling for multi core */
143 g_cond_broadcast (tcond);
144 }
145 /* call multiple (unoptimized) initializers from multiple threads */
146 g_mutex_lock (tmutex);
147 g_atomic_int_set (&thread_call_count, 0);
148 for (i = 0; i < N_THREADS; i++)
149 g_thread_create (stress_concurrent_initializers, 0, FALSE, NULL);
150 g_mutex_unlock (tmutex);
151 while (g_atomic_int_get (&thread_call_count) < 256 * 4 * N_THREADS)
152 g_usleep (50 * 1000); /* wait for all 5 threads to complete */
153 return 0;
154 }
155
156 /* get rid of g_once_init_enter-optimizations in the below definitions
157 * to uncover possible races in the g_once_init_enter_impl()/
158 * g_once_init_leave() implementations
159 */
160 #define g_once_init_enter g_once_init_enter_impl
161
162 /* define 16 * 16 simple initializers */
163 #define DEFINE_TEST_INITIALIZER(N) \
164 static void \
165 test_initializer_##N (void) \
166 { \
167 static volatile gsize initialized = 0; \
168 if (g_once_init_enter (&initialized)) \
169 { \
170 g_free (g_strdup_printf ("cpuhog%5d", 1)); \
171 g_free (g_strdup_printf ("cpuhog%6d", 2)); \
172 g_free (g_strdup_printf ("cpuhog%7d", 3)); \
173 g_once_init_leave (&initialized, 1); \
174 } \
175 }
176 #define DEFINE_16_TEST_INITIALIZERS(P) \
177 DEFINE_TEST_INITIALIZER (P##0) \
178 DEFINE_TEST_INITIALIZER (P##1) \
179 DEFINE_TEST_INITIALIZER (P##2) \
180 DEFINE_TEST_INITIALIZER (P##3) \
181 DEFINE_TEST_INITIALIZER (P##4) \
182 DEFINE_TEST_INITIALIZER (P##5) \
183 DEFINE_TEST_INITIALIZER (P##6) \
184 DEFINE_TEST_INITIALIZER (P##7) \
185 DEFINE_TEST_INITIALIZER (P##8) \
186 DEFINE_TEST_INITIALIZER (P##9) \
187 DEFINE_TEST_INITIALIZER (P##a) \
188 DEFINE_TEST_INITIALIZER (P##b) \
189 DEFINE_TEST_INITIALIZER (P##c) \
190 DEFINE_TEST_INITIALIZER (P##d) \
191 DEFINE_TEST_INITIALIZER (P##e) \
192 DEFINE_TEST_INITIALIZER (P##f)
193 #define DEFINE_256_TEST_INITIALIZERS(P) \
194 DEFINE_16_TEST_INITIALIZERS (P##_0) \
195 DEFINE_16_TEST_INITIALIZERS (P##_1) \
196 DEFINE_16_TEST_INITIALIZERS (P##_2) \
197 DEFINE_16_TEST_INITIALIZERS (P##_3) \
198 DEFINE_16_TEST_INITIALIZERS (P##_4) \
199 DEFINE_16_TEST_INITIALIZERS (P##_5) \
200 DEFINE_16_TEST_INITIALIZERS (P##_6) \
201 DEFINE_16_TEST_INITIALIZERS (P##_7) \
202 DEFINE_16_TEST_INITIALIZERS (P##_8) \
203 DEFINE_16_TEST_INITIALIZERS (P##_9) \
204 DEFINE_16_TEST_INITIALIZERS (P##_a) \
205 DEFINE_16_TEST_INITIALIZERS (P##_b) \
206 DEFINE_16_TEST_INITIALIZERS (P##_c) \
207 DEFINE_16_TEST_INITIALIZERS (P##_d) \
208 DEFINE_16_TEST_INITIALIZERS (P##_e) \
209 DEFINE_16_TEST_INITIALIZERS (P##_f)
210
211 /* list 16 * 16 simple initializers */
212 #define LIST_16_TEST_INITIALIZERS(P) \
213 test_initializer_##P##0, \
214 test_initializer_##P##1, \
215 test_initializer_##P##2, \
216 test_initializer_##P##3, \
217 test_initializer_##P##4, \
218 test_initializer_##P##5, \
219 test_initializer_##P##6, \
220 test_initializer_##P##7, \
221 test_initializer_##P##8, \
222 test_initializer_##P##9, \
223 test_initializer_##P##a, \
224 test_initializer_##P##b, \
225 test_initializer_##P##c, \
226 test_initializer_##P##d, \
227 test_initializer_##P##e, \
228 test_initializer_##P##f
229 #define LIST_256_TEST_INITIALIZERS(P) \
230 LIST_16_TEST_INITIALIZERS (P##_0), \
231 LIST_16_TEST_INITIALIZERS (P##_1), \
232 LIST_16_TEST_INITIALIZERS (P##_2), \
233 LIST_16_TEST_INITIALIZERS (P##_3), \
234 LIST_16_TEST_INITIALIZERS (P##_4), \
235 LIST_16_TEST_INITIALIZERS (P##_5), \
236 LIST_16_TEST_INITIALIZERS (P##_6), \
237 LIST_16_TEST_INITIALIZERS (P##_7), \
238 LIST_16_TEST_INITIALIZERS (P##_8), \
239 LIST_16_TEST_INITIALIZERS (P##_9), \
240 LIST_16_TEST_INITIALIZERS (P##_a), \
241 LIST_16_TEST_INITIALIZERS (P##_b), \
242 LIST_16_TEST_INITIALIZERS (P##_c), \
243 LIST_16_TEST_INITIALIZERS (P##_d), \
244 LIST_16_TEST_INITIALIZERS (P##_e), \
245 LIST_16_TEST_INITIALIZERS (P##_f)
246
247 /* define 4 * 256 initializers */
248 DEFINE_256_TEST_INITIALIZERS (stress1);
249 DEFINE_256_TEST_INITIALIZERS (stress2);
250 DEFINE_256_TEST_INITIALIZERS (stress3);
251 DEFINE_256_TEST_INITIALIZERS (stress4);
252
253 /* call the above 1024 initializers */
254 static void*
stress_concurrent_initializers(void * user_data)255 stress_concurrent_initializers (void *user_data)
256 {
257 static void (*initializers[]) (void) = {
258 LIST_256_TEST_INITIALIZERS (stress1),
259 LIST_256_TEST_INITIALIZERS (stress2),
260 LIST_256_TEST_INITIALIZERS (stress3),
261 LIST_256_TEST_INITIALIZERS (stress4),
262 };
263 int i;
264 /* sync to main thread */
265 g_mutex_lock (tmutex);
266 g_mutex_unlock (tmutex);
267 /* initialize concurrently */
268 for (i = 0; i < G_N_ELEMENTS (initializers); i++)
269 {
270 initializers[i]();
271 g_atomic_int_exchange_and_add (&thread_call_count, 1);
272 }
273 return NULL;
274 }
275