LCOV - code coverage report
Current view: top level - glib/gobject/tests - closure-refcount.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 108 128 84.4 %
Date: 2024-04-30 05:17:35 Functions: 16 18 88.9 %
Branches: 22 30 73.3 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright (C) 2005 Imendio AB
       2                 :            :  *
       3                 :            :  * SPDX-License-Identifier: LicenseRef-old-glib-tests
       4                 :            :  *
       5                 :            :  * This software is provided "as is"; redistribution and modification
       6                 :            :  * is permitted, provided that the following disclaimer is retained.
       7                 :            :  *
       8                 :            :  * This software 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                 :            :  * In no event shall the authors or contributors be liable for any
      12                 :            :  * direct, indirect, incidental, special, exemplary, or consequential
      13                 :            :  * damages (including, but not limited to, procurement of substitute
      14                 :            :  * goods or services; loss of use, data, or profits; or business
      15                 :            :  * interruption) however caused and on any theory of liability, whether
      16                 :            :  * in contract, strict liability, or tort (including negligence or
      17                 :            :  * otherwise) arising in any way out of the use of this software, even
      18                 :            :  * if advised of the possibility of such damage.
      19                 :            :  */
      20                 :            : #include <glib-object.h>
      21                 :            : 
      22                 :            : #ifdef G_OS_UNIX
      23                 :            : #include <unistd.h>
      24                 :            : #endif
      25                 :            : 
      26                 :            : #define TEST_POINTER1   ((gpointer) 47)
      27                 :            : #define TEST_POINTER2   ((gpointer) 49)
      28                 :            : #define TEST_INT1       (-77)
      29                 :            : #define TEST_INT2       (78)
      30                 :            : 
      31                 :            : /* --- GTest class --- */
      32                 :            : typedef struct {
      33                 :            :   GObject object;
      34                 :            :   gint value;
      35                 :            :   gpointer test_pointer1;
      36                 :            :   gpointer test_pointer2;
      37                 :            : } GTest;
      38                 :            : typedef struct {
      39                 :            :   GObjectClass parent_class;
      40                 :            :   void (*test_signal1) (GTest * test, gint an_int);
      41                 :            :   void (*test_signal2) (GTest * test, gint an_int);
      42                 :            : } GTestClass;
      43                 :            : 
      44                 :            : #define G_TYPE_TEST                (my_test_get_type ())
      45                 :            : #define MY_TEST(test)              (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
      46                 :            : #define MY_IS_TEST(test)           (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
      47                 :            : #define MY_TEST_CLASS(tclass)      (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
      48                 :            : #define MY_IS_TEST_CLASS(tclass)   (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
      49                 :            : #define MY_TEST_GET_CLASS(test)    (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
      50                 :            : 
      51                 :            : static GType my_test_get_type (void);
      52   [ +  +  +  -  :          3 : G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
                   +  - ]
      53                 :            : 
      54                 :            : /* Test state */
      55                 :            : typedef struct
      56                 :            : {
      57                 :            :   GClosure *closure;  /* (unowned) */
      58                 :            :   gboolean stopping;
      59                 :            :   gboolean seen_signal_handler;
      60                 :            :   gboolean seen_cleanup;
      61                 :            :   gboolean seen_test_int1;
      62                 :            :   gboolean seen_test_int2;
      63                 :            :   gboolean seen_thread1;
      64                 :            :   gboolean seen_thread2;
      65                 :            : } TestClosureRefcountData;
      66                 :            : 
      67                 :            : /* --- functions --- */
      68                 :            : static void
      69                 :          1 : my_test_init (GTest * test)
      70                 :            : {
      71                 :          1 :   g_test_message ("Init %p", test);
      72                 :            : 
      73                 :          1 :   test->value = 0;
      74                 :          1 :   test->test_pointer1 = TEST_POINTER1;
      75                 :          1 :   test->test_pointer2 = TEST_POINTER2;
      76                 :          1 : }
      77                 :            : 
      78                 :            : typedef enum
      79                 :            : {
      80                 :            :   PROP_TEST_PROP = 1,
      81                 :            : } MyTestProperty;
      82                 :            : 
      83                 :            : typedef enum
      84                 :            : {
      85                 :            :   SIGNAL_TEST_SIGNAL1,
      86                 :            :   SIGNAL_TEST_SIGNAL2,
      87                 :            : } MyTestSignal;
      88                 :            : 
      89                 :            : static guint signals[SIGNAL_TEST_SIGNAL2 + 1] = { 0, };
      90                 :            : 
      91                 :            : static void
      92                 :          0 : my_test_set_property (GObject      *object,
      93                 :            :                      guint         prop_id,
      94                 :            :                      const GValue *value,
      95                 :            :                      GParamSpec   *pspec)
      96                 :            : {
      97                 :          0 :   GTest *test = MY_TEST (object);
      98         [ #  # ]:          0 :   switch (prop_id)
      99                 :            :     {
     100                 :          0 :     case PROP_TEST_PROP:
     101                 :          0 :       test->value = g_value_get_int (value);
     102                 :          0 :       break;
     103                 :          0 :     default:
     104                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     105                 :          0 :       break;
     106                 :            :     }
     107                 :          0 : }
     108                 :            : 
     109                 :            : static void
     110                 :          0 : my_test_get_property (GObject    *object,
     111                 :            :                      guint       prop_id,
     112                 :            :                      GValue     *value,
     113                 :            :                      GParamSpec *pspec)
     114                 :            : {
     115                 :          0 :   GTest *test = MY_TEST (object);
     116         [ #  # ]:          0 :   switch (prop_id)
     117                 :            :     {
     118                 :          0 :     case PROP_TEST_PROP:
     119                 :          0 :       g_value_set_int (value, test->value);
     120                 :          0 :       break;
     121                 :          0 :     default:
     122                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     123                 :          0 :       break;
     124                 :            :     }
     125                 :          0 : }
     126                 :            : 
     127                 :            : static void
     128                 :     999999 : my_test_test_signal2 (GTest *test,
     129                 :            :                       gint   an_int)
     130                 :            : {
     131                 :     999999 : }
     132                 :            : 
     133                 :            : static void
     134                 :     999999 : my_test_emit_test_signal1 (GTest *test,
     135                 :            :                            gint   vint)
     136                 :            : {
     137                 :     999999 :   g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL1], 0, vint);
     138                 :     999999 : }
     139                 :            : 
     140                 :            : static void
     141                 :     999999 : my_test_emit_test_signal2 (GTest *test,
     142                 :            :                            gint   vint)
     143                 :            : {
     144                 :     999999 :   g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL2], 0, vint);
     145                 :     999999 : }
     146                 :            : 
     147                 :            : static void
     148                 :          1 : my_test_class_init (GTestClass *klass)
     149                 :            : {
     150                 :          1 :   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     151                 :            : 
     152                 :          1 :   gobject_class->set_property = my_test_set_property;
     153                 :          1 :   gobject_class->get_property = my_test_get_property;
     154                 :            : 
     155                 :          1 :   signals[SIGNAL_TEST_SIGNAL1] =
     156                 :          1 :       g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
     157                 :            :                     G_STRUCT_OFFSET (GTestClass, test_signal1), NULL, NULL,
     158                 :            :                     g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
     159                 :          1 :   signals[SIGNAL_TEST_SIGNAL2] =
     160                 :          1 :       g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
     161                 :            :                     G_STRUCT_OFFSET (GTestClass, test_signal2), NULL, NULL,
     162                 :            :                     g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
     163                 :            : 
     164                 :          1 :   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEST_PROP,
     165                 :            :                                    g_param_spec_int ("test-prop", "Test Prop", "Test property",
     166                 :            :                                                      0, 1, 0, G_PARAM_READWRITE));
     167                 :          1 :   klass->test_signal2 = my_test_test_signal2;
     168                 :          1 : }
     169                 :            : 
     170                 :            : static void
     171                 :     409480 : test_closure (GClosure *closure)
     172                 :            : {
     173                 :            :   /* try to produce high contention in closure->ref_count */
     174                 :     409480 :   guint i = 0, n = g_random_int () % 199;
     175         [ +  + ]:   40912074 :   for (i = 0; i < n; i++)
     176                 :   40502594 :     g_closure_ref (closure);
     177                 :     409480 :   g_closure_sink (closure); /* NOP */
     178         [ +  + ]:   40912074 :   for (i = 0; i < n; i++)
     179                 :   40502594 :     g_closure_unref (closure);
     180                 :     409480 : }
     181                 :            : 
     182                 :            : static gpointer
     183                 :          1 : thread1_main (gpointer user_data)
     184                 :            : {
     185                 :          1 :   TestClosureRefcountData *data = user_data;
     186                 :          1 :   guint i = 0;
     187                 :            : 
     188         [ +  + ]:     208764 :   for (i = 1; !g_atomic_int_get (&data->stopping); i++)
     189                 :            :     {
     190                 :     208763 :       test_closure (data->closure);
     191         [ +  + ]:     208763 :       if (i % 10000 == 0)
     192                 :            :         {
     193                 :         20 :           g_test_message ("Yielding from thread1");
     194                 :         20 :           g_thread_yield (); /* force context switch */
     195                 :         20 :           g_atomic_int_set (&data->seen_thread1, TRUE);
     196                 :            :         }
     197                 :            :     }
     198                 :          1 :   return NULL;
     199                 :            : }
     200                 :            : 
     201                 :            : static gpointer
     202                 :          1 : thread2_main (gpointer user_data)
     203                 :            : {
     204                 :          1 :   TestClosureRefcountData *data = user_data;
     205                 :          1 :   guint i = 0;
     206                 :            : 
     207         [ +  + ]:     200718 :   for (i = 1; !g_atomic_int_get (&data->stopping); i++)
     208                 :            :     {
     209                 :     200717 :       test_closure (data->closure);
     210         [ +  + ]:     200717 :       if (i % 10000 == 0)
     211                 :            :         {
     212                 :         20 :           g_test_message ("Yielding from thread2");
     213                 :         20 :           g_thread_yield (); /* force context switch */
     214                 :         20 :           g_atomic_int_set (&data->seen_thread2, TRUE);
     215                 :            :         }
     216                 :            :     }
     217                 :          1 :   return NULL;
     218                 :            : }
     219                 :            : 
     220                 :            : static void
     221                 :    1999998 : test_signal_handler (GTest   *test,
     222                 :            :                      gint     vint,
     223                 :            :                      gpointer user_data)
     224                 :            : {
     225                 :    1999998 :   TestClosureRefcountData *data = user_data;
     226                 :            : 
     227                 :    1999998 :   g_assert_true (test->test_pointer1 == TEST_POINTER1);
     228                 :            : 
     229                 :    1999998 :   data->seen_signal_handler = TRUE;
     230                 :    1999998 :   data->seen_test_int1 |= vint == TEST_INT1;
     231                 :    1999998 :   data->seen_test_int2 |= vint == TEST_INT2;
     232                 :    1999998 : }
     233                 :            : 
     234                 :            : static void
     235                 :          1 : destroy_data (gpointer  user_data,
     236                 :            :               GClosure *closure)
     237                 :            : {
     238                 :          1 :   TestClosureRefcountData *data = user_data;
     239                 :            : 
     240                 :          1 :   data->seen_cleanup = TRUE;
     241                 :          1 :   g_assert_true (data->closure == closure);
     242                 :          1 :   g_assert_cmpint (closure->ref_count, ==, 0);
     243                 :          1 : }
     244                 :            : 
     245                 :            : static void
     246                 :     999999 : test_emissions (GTest *test)
     247                 :            : {
     248                 :     999999 :   my_test_emit_test_signal1 (test, TEST_INT1);
     249                 :     999999 :   my_test_emit_test_signal2 (test, TEST_INT2);
     250                 :     999999 : }
     251                 :            : 
     252                 :            : /* Test that closure refcounting works even when high contested between three
     253                 :            :  * threads (the main thread, thread1 and thread2). Both child threads are
     254                 :            :  * contesting refs/unrefs, while the main thread periodically emits signals
     255                 :            :  * which also do refs/unrefs on closures. */
     256                 :            : static void
     257                 :          1 : test_closure_refcount (void)
     258                 :            : {
     259                 :            :   GThread *thread1, *thread2;
     260                 :          1 :   TestClosureRefcountData test_data = { 0, };
     261                 :            :   GClosure *closure;
     262                 :            :   GTest *object;
     263                 :            :   guint i, n_iterations;
     264                 :            : 
     265                 :          1 :   object = g_object_new (G_TYPE_TEST, NULL);
     266                 :          1 :   closure = g_cclosure_new (G_CALLBACK (test_signal_handler), &test_data, destroy_data);
     267                 :            : 
     268                 :          1 :   g_signal_connect_closure (object, "test-signal1", closure, FALSE);
     269                 :          1 :   g_signal_connect_closure (object, "test-signal2", closure, FALSE);
     270                 :            : 
     271                 :          1 :   test_data.stopping = FALSE;
     272                 :          1 :   test_data.closure = closure;
     273                 :            : 
     274                 :          1 :   thread1 = g_thread_new ("thread1", thread1_main, &test_data);
     275                 :          1 :   thread2 = g_thread_new ("thread2", thread2_main, &test_data);
     276                 :            : 
     277                 :            :   /* The 16-bit compare-and-swap operations currently used for closure
     278                 :            :    * refcounts are really slow on some ARM CPUs, notably Cortex-A57.
     279                 :            :    * Reduce the number of iterations so that the test completes in a
     280                 :            :    * finite time, but don't reduce it so much that the main thread
     281                 :            :    * starves the other threads and causes a test failure.
     282                 :            :    *
     283                 :            :    * https://gitlab.gnome.org/GNOME/glib/issues/1316
     284                 :            :    * aka https://bugs.debian.org/880883 */
     285                 :            : #if defined(__aarch64__) || defined(__arm__)
     286                 :            :   n_iterations = 100000;
     287                 :            : #else
     288                 :          1 :   n_iterations = 1000000;
     289                 :            : #endif
     290                 :            : 
     291                 :            :   /* Run the test for a reasonably high number of iterations, and ensure we
     292                 :            :    * don’t terminate until at least 10000 iterations have completed in both
     293                 :            :    * thread1 and thread2. Even though @n_iterations is high, we can’t guarantee
     294                 :            :    * that the scheduler allocates time fairly (or at all!) to thread1 or
     295                 :            :    * thread2. */
     296                 :          1 :   for (i = 1;
     297                 :          1 :        i < n_iterations ||
     298   [ +  +  -  + ]:    1000000 :        !g_atomic_int_get (&test_data.seen_thread1) ||
     299         [ -  + ]:          1 :        !g_atomic_int_get (&test_data.seen_thread2);
     300                 :     999999 :        i++)
     301                 :            :     {
     302                 :     999999 :       test_emissions (object);
     303         [ +  + ]:     999999 :       if (i % 10000 == 0)
     304                 :            :         {
     305                 :         99 :           g_test_message ("Yielding from main thread");
     306                 :         99 :           g_thread_yield (); /* force context switch */
     307                 :            :         }
     308                 :            :     }
     309                 :            : 
     310                 :          1 :   g_atomic_int_set (&test_data.stopping, TRUE);
     311                 :          1 :   g_test_message ("Stopping");
     312                 :            : 
     313                 :            :   /* wait for thread shutdown */
     314                 :          1 :   g_thread_join (thread1);
     315                 :          1 :   g_thread_join (thread2);
     316                 :            : 
     317                 :            :   /* finalize object, destroy signals, run cleanup code */
     318                 :          1 :   g_object_unref (object);
     319                 :            : 
     320                 :          1 :   g_test_message ("Stopped");
     321                 :            : 
     322                 :          1 :   g_assert_true (g_atomic_int_get (&test_data.seen_thread1));
     323                 :          1 :   g_assert_true (g_atomic_int_get (&test_data.seen_thread2));
     324                 :          1 :   g_assert_true (test_data.seen_test_int1);
     325                 :          1 :   g_assert_true (test_data.seen_test_int2);
     326                 :          1 :   g_assert_true (test_data.seen_signal_handler);
     327                 :          1 :   g_assert_true (test_data.seen_cleanup);
     328                 :          1 : }
     329                 :            : 
     330                 :            : int
     331                 :          1 : main (int argc,
     332                 :            :       char *argv[])
     333                 :            : {
     334                 :          1 :   g_test_init (&argc, &argv, NULL);
     335                 :            : 
     336                 :          1 :   g_test_add_func ("/closure/refcount", test_closure_refcount);
     337                 :            : 
     338                 :          1 :   return g_test_run ();
     339                 :            : }

Generated by: LCOV version 1.14