Home | History | Annotate | Download | only in tests
      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