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 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 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 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 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 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 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 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 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* 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