• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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