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