LCOV - code coverage report
Current view: top level - glib/glib/tests - once.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 75 75 100.0 %
Date: 2024-04-16 05:15:53 Functions: 11 11 100.0 %
Branches: 27 40 67.5 %

           Branch data     Line data    Source code
       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                 :          1 : do_once (gpointer data)
      37                 :            : {
      38                 :            :   static gint i = 0;
      39                 :            : 
      40                 :          1 :   i++;
      41                 :            : 
      42                 :          1 :   return GINT_TO_POINTER (i);
      43                 :            : }
      44                 :            : 
      45                 :            : static void
      46                 :          1 : test_once_single_threaded (void)
      47                 :            : {
      48                 :          1 :   GOnce once = G_ONCE_INIT;
      49                 :            :   gpointer res;
      50                 :            : 
      51                 :          1 :   g_test_summary ("Test g_once() usage from a single thread");
      52                 :            : 
      53                 :          1 :   g_assert_cmpint (once.status, ==, G_ONCE_STATUS_NOTCALLED);
      54                 :            : 
      55         [ -  + ]:          1 :   res = g_once (&once, do_once, NULL);
      56                 :          1 :   g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
      57                 :            : 
      58                 :          1 :   g_assert_cmpint (once.status, ==, G_ONCE_STATUS_READY);
      59                 :            : 
      60         [ +  - ]:          1 :   res = g_once (&once, do_once, NULL);
      61                 :          1 :   g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
      62                 :          1 : }
      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                 :          1 : 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                 :          1 :   old_value = g_atomic_int_add (&once_multi_threaded_counter, 1);
      80                 :            : 
      81                 :          1 :   return GINT_TO_POINTER (old_value + 1);
      82                 :            : }
      83                 :            : 
      84                 :            : static gpointer
      85                 :        100 : once_thread_func (gpointer data)
      86                 :            : {
      87                 :            :   gpointer res;
      88                 :        100 :   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                 :        100 :   g_mutex_lock (&once_multi_threaded_mutex);
      94                 :            : 
      95                 :        100 :   once_multi_threaded_n_threads_waiting++;
      96                 :        100 :   g_cond_broadcast (&once_multi_threaded_cond);
      97                 :            : 
      98         [ +  + ]:       1122 :   while (once_multi_threaded_n_threads_waiting < n_threads_expected)
      99                 :       1022 :     g_cond_wait (&once_multi_threaded_cond, &once_multi_threaded_mutex);
     100                 :        100 :   g_mutex_unlock (&once_multi_threaded_mutex);
     101                 :            : 
     102                 :            :   /* Actually run the test. */
     103         [ +  + ]:        100 :   res = g_once (&once_multi_threaded, do_once_multi_threaded, NULL);
     104                 :        100 :   g_assert_cmpint (GPOINTER_TO_INT (res), ==, 1);
     105                 :            : 
     106                 :        100 :   return NULL;
     107                 :            : }
     108                 :            : 
     109                 :            : static void
     110                 :          1 : test_once_multi_threaded (void)
     111                 :            : {
     112                 :            :   guint i;
     113                 :            :   GThread *threads[THREADS];
     114                 :            : 
     115                 :          1 :   g_test_summary ("Test g_once() usage from multiple threads");
     116                 :            : 
     117         [ +  + ]:        101 :   for (i = 0; i < G_N_ELEMENTS (threads); i++)
     118                 :        100 :     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                 :          1 :   g_cond_broadcast (&once_multi_threaded_cond);
     124                 :            : 
     125         [ +  + ]:        101 :   for (i = 0; i < G_N_ELEMENTS (threads); i++)
     126                 :        100 :     g_thread_join (threads[i]);
     127                 :            : 
     128                 :          1 :   g_assert_cmpint (g_atomic_int_get (&once_multi_threaded_counter), ==, 1);
     129                 :          1 : }
     130                 :            : 
     131                 :            : static void
     132                 :          1 : test_once_init_single_threaded (void)
     133                 :            : {
     134                 :            :   static gsize init = 0;
     135                 :            : 
     136                 :          1 :   g_test_summary ("Test g_once_init_{enter,leave}() usage from a single thread");
     137                 :            : 
     138   [ +  -  +  -  :          1 :   if (g_once_init_enter (&init))
                   +  - ]
     139                 :            :     {
     140                 :            :       g_assert (TRUE);
     141                 :          1 :       g_once_init_leave (&init, 1);
     142                 :            :     }
     143                 :            : 
     144                 :          1 :   g_assert_cmpint (init, ==, 1);
     145   [ -  +  -  -  :          1 :   if (g_once_init_enter (&init))
                   -  + ]
     146                 :            :     {
     147                 :            :       g_assert_not_reached ();
     148                 :            :       g_once_init_leave (&init, 2);
     149                 :            :     }
     150                 :          1 :   g_assert_cmpint (init, ==, 1);
     151                 :          1 : }
     152                 :            : 
     153                 :            : static gint64 shared;
     154                 :            : 
     155                 :            : static void
     156                 :        100 : init_shared (void)
     157                 :            : {
     158                 :            :   static gsize init = 0;
     159                 :            : 
     160   [ +  +  +  -  :        100 :   if (g_once_init_enter (&init))
                   +  + ]
     161                 :            :     {
     162                 :          1 :       shared += 42;
     163                 :            : 
     164                 :          1 :       g_once_init_leave (&init, 1);
     165                 :            :     }
     166                 :        100 : }
     167                 :            : 
     168                 :            : static gpointer
     169                 :        100 : thread_func (gpointer data)
     170                 :            : {
     171                 :        100 :   init_shared ();
     172                 :            : 
     173                 :        100 :   return NULL;
     174                 :            : }
     175                 :            : 
     176                 :            : static void
     177                 :          1 : test_once_init_multi_threaded (void)
     178                 :            : {
     179                 :            :   gsize i;
     180                 :            :   GThread *threads[THREADS];
     181                 :            : 
     182                 :          1 :   g_test_summary ("Test g_once_init_{enter,leave}() usage from multiple threads");
     183                 :            : 
     184                 :          1 :   shared = 0;
     185                 :            : 
     186         [ +  + ]:        101 :   for (i = 0; i < G_N_ELEMENTS (threads); i++)
     187                 :        100 :     threads[i] = g_thread_new ("once-init-multi-threaded", thread_func, NULL);
     188                 :            : 
     189         [ +  + ]:        101 :   for (i = 0; i < G_N_ELEMENTS (threads); i++)
     190                 :        100 :     g_thread_join (threads[i]);
     191                 :            : 
     192                 :          1 :   g_assert_cmpint (shared, ==, 42);
     193                 :          1 : }
     194                 :            : 
     195                 :            : static void
     196                 :          1 : test_once_init_string (void)
     197                 :            : {
     198                 :            :   static gchar *val;
     199                 :            : 
     200                 :          1 :   g_test_summary ("Test g_once_init_{enter,leave}() usage with a string");
     201                 :            : 
     202   [ +  -  +  -  :          1 :   if (g_once_init_enter_pointer (&val))
                   +  - ]
     203                 :          1 :     g_once_init_leave_pointer (&val, "foo");
     204                 :            : 
     205                 :          1 :   g_assert_cmpstr (val, ==, "foo");
     206                 :          1 : }
     207                 :            : 
     208                 :            : int
     209                 :          1 : main (int argc, char *argv[])
     210                 :            : {
     211                 :          1 :   g_test_init (&argc, &argv, NULL);
     212                 :            : 
     213                 :          1 :   g_test_add_func ("/once/single-threaded", test_once_single_threaded);
     214                 :          1 :   g_test_add_func ("/once/multi-threaded", test_once_multi_threaded);
     215                 :          1 :   g_test_add_func ("/once-init/single-threaded", test_once_init_single_threaded);
     216                 :          1 :   g_test_add_func ("/once-init/multi-threaded", test_once_init_multi_threaded);
     217                 :          1 :   g_test_add_func ("/once-init/string", test_once_init_string);
     218                 :            : 
     219                 :          1 :   return g_test_run ();
     220                 :            : }

Generated by: LCOV version 1.14