(root)/
glib-2.79.0/
glib/
tests/
once.c
       1  
       2  /* Unit tests for GOnce and friends
       3   * Copyright (C) 2011 Red Hat, Inc
       4   * Author: Matthias Clasen
       5   *
       6   * SPDX-License-Identifier: LicenseRef-old-glib-tests
       7   *
       8   * This work is provided "as is"; redistribution and modification
       9   * in whole or in part, in any medium, physical or electronic is
      10   * permitted without restriction.
      11   *
      12   * This work is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      15   *
      16   * In no event shall the authors or contributors be liable for any
      17   * direct, indirect, incidental, special, exemplary, or consequential
      18   * damages (including, but not limited to, procurement of substitute
      19   * goods or services; loss of use, data, or profits; or business
      20   * interruption) however caused and on any theory of liability, whether
      21   * in contract, strict liability, or tort (including negligence or
      22   * otherwise) arising in any way out of the use of this software, even
      23   * if advised of the possibility of such damage.
      24   */
      25  
      26  #include <glib.h>
      27  #include "../gvalgrind.h"
      28  
      29  #if GLIB_SIZEOF_VOID_P > 4 && !defined(ENABLE_VALGRIND)
      30  #define THREADS 1000
      31  #else
      32  #define THREADS 100
      33  #endif
      34  
      35  static gpointer
      36  do_once (gpointer data)
      37  {
      38    static gint i = 0;
      39  
      40    i++;
      41  
      42    return GINT_TO_POINTER (i);
      43  }
      44  
      45  static void
      46  test_once_single_threaded (void)
      47  {
      48    GOnce once = G_ONCE_INIT;
      49    gpointer res;
      50  
      51    g_test_summary ("Test g_once() usage from a single thread");
      52  
      53    g_assert_cmpint (once.status, ==, G_ONCE_STATUS_NOTCALLED);
      54  
      55    res = g_once (&once, do_once, NULL);
      56    g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
      57  
      58    g_assert_cmpint (once.status, ==, G_ONCE_STATUS_READY);
      59  
      60    res = g_once (&once, do_once, NULL);
      61    g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
      62  }
      63  
      64  static GOnce once_multi_threaded = G_ONCE_INIT;
      65  static gint once_multi_threaded_counter = 0;
      66  static GCond once_multi_threaded_cond;
      67  static GMutex once_multi_threaded_mutex;
      68  static guint once_multi_threaded_n_threads_waiting = 0;
      69  
      70  static gpointer
      71  do_once_multi_threaded (gpointer data)
      72  {
      73    gint old_value;
      74  
      75    /* While this function should only ever be executed once, by one thread,
      76     * we should use atomics to ensure that if there were a bug, writes to
      77     * `once_multi_threaded_counter` from multiple threads would not get lost and
      78     * mean the test erroneously succeeded. */
      79    old_value = g_atomic_int_add (&once_multi_threaded_counter, 1);
      80  
      81    return GINT_TO_POINTER (old_value + 1);
      82  }
      83  
      84  static gpointer
      85  once_thread_func (gpointer data)
      86  {
      87    gpointer res;
      88    guint n_threads_expected = GPOINTER_TO_UINT (data);
      89  
      90    /* Don’t immediately call g_once(), otherwise the first thread to be created
      91     * will end up calling the once-function, and there will be very little
      92     * contention. */
      93    g_mutex_lock (&once_multi_threaded_mutex);
      94  
      95    once_multi_threaded_n_threads_waiting++;
      96    g_cond_broadcast (&once_multi_threaded_cond);
      97  
      98    while (once_multi_threaded_n_threads_waiting < n_threads_expected)
      99      g_cond_wait (&once_multi_threaded_cond, &once_multi_threaded_mutex);
     100    g_mutex_unlock (&once_multi_threaded_mutex);
     101  
     102    /* Actually run the test. */
     103    res = g_once (&once_multi_threaded, do_once_multi_threaded, NULL);
     104    g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
     105  
     106    return NULL;
     107  }
     108  
     109  static void
     110  test_once_multi_threaded (void)
     111  {
     112    guint i;
     113    GThread *threads[THREADS];
     114  
     115    g_test_summary ("Test g_once() usage from multiple threads");
     116  
     117    for (i = 0; i < G_N_ELEMENTS (threads); i++)
     118      threads[i] = g_thread_new ("once-multi-threaded",
     119                                 once_thread_func,
     120                                 GUINT_TO_POINTER (G_N_ELEMENTS (threads)));
     121  
     122    /* All threads have started up, so start the test. */
     123    g_cond_broadcast (&once_multi_threaded_cond);
     124  
     125    for (i = 0; i < G_N_ELEMENTS (threads); i++)
     126      g_thread_join (threads[i]);
     127  
     128    g_assert_cmpint (g_atomic_int_get (&once_multi_threaded_counter), ==, 1);
     129  }
     130  
     131  static void
     132  test_once_init_single_threaded (void)
     133  {
     134    static gsize init = 0;
     135  
     136    g_test_summary ("Test g_once_init_{enter,leave}() usage from a single thread");
     137  
     138    if (g_once_init_enter (&init))
     139      {
     140        g_assert (TRUE);
     141        g_once_init_leave (&init, 1);
     142      }
     143  
     144    g_assert_cmpint (init, ==, 1);
     145    if (g_once_init_enter (&init))
     146      {
     147        g_assert_not_reached ();
     148        g_once_init_leave (&init, 2);
     149      }
     150    g_assert_cmpint (init, ==, 1);
     151  }
     152  
     153  static gint64 shared;
     154  
     155  static void
     156  init_shared (void)
     157  {
     158    static gsize init = 0;
     159  
     160    if (g_once_init_enter (&init))
     161      {
     162        shared += 42;
     163  
     164        g_once_init_leave (&init, 1);
     165      }
     166  }
     167  
     168  static gpointer
     169  thread_func (gpointer data)
     170  {
     171    init_shared ();
     172  
     173    return NULL;
     174  }
     175  
     176  static void
     177  test_once_init_multi_threaded (void)
     178  {
     179    gsize i;
     180    GThread *threads[THREADS];
     181  
     182    g_test_summary ("Test g_once_init_{enter,leave}() usage from multiple threads");
     183  
     184    shared = 0;
     185  
     186    for (i = 0; i < G_N_ELEMENTS (threads); i++)
     187      threads[i] = g_thread_new ("once-init-multi-threaded", thread_func, NULL);
     188  
     189    for (i = 0; i < G_N_ELEMENTS (threads); i++)
     190      g_thread_join (threads[i]);
     191  
     192    g_assert_cmpint (shared, ==, 42);
     193  }
     194  
     195  static void
     196  test_once_init_string (void)
     197  {
     198    static gchar *val;
     199  
     200    g_test_summary ("Test g_once_init_{enter,leave}() usage with a string");
     201  
     202    if (g_once_init_enter_pointer (&val))
     203      g_once_init_leave_pointer (&val, "foo");
     204  
     205    g_assert_cmpstr (val, ==, "foo");
     206  }
     207  
     208  int
     209  main (int argc, char *argv[])
     210  {
     211    g_test_init (&argc, &argv, NULL);
     212  
     213    g_test_add_func ("/once/single-threaded", test_once_single_threaded);
     214    g_test_add_func ("/once/multi-threaded", test_once_multi_threaded);
     215    g_test_add_func ("/once-init/single-threaded", test_once_init_single_threaded);
     216    g_test_add_func ("/once-init/multi-threaded", test_once_init_multi_threaded);
     217    g_test_add_func ("/once-init/string", test_once_init_string);
     218  
     219    return g_test_run ();
     220  }