LCOV - code coverage report
Current view: top level - gobject/tests - closure-refcount.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 84.4 % 128 108
Test Date: 2024-11-26 05:23:01 Functions: 88.9 % 18 16
Branches: - 0 0

             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                 :      698035 : test_closure (GClosure *closure)
     172                 :             : {
     173                 :             :   /* try to produce high contention in closure->ref_count */
     174                 :      698035 :   guint i = 0, n = g_random_int () % 199;
     175                 :    69793979 :   for (i = 0; i < n; i++)
     176                 :    69095944 :     g_closure_ref (closure);
     177                 :      698035 :   g_closure_sink (closure); /* NOP */
     178                 :    69793979 :   for (i = 0; i < n; i++)
     179                 :    69095944 :     g_closure_unref (closure);
     180                 :      698035 : }
     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                 :      338293 :   for (i = 1; !g_atomic_int_get (&data->stopping); i++)
     189                 :             :     {
     190                 :      338292 :       test_closure (data->closure);
     191                 :      338292 :       if (i % 10000 == 0)
     192                 :             :         {
     193                 :          33 :           g_test_message ("Yielding from thread1");
     194                 :          33 :           g_thread_yield (); /* force context switch */
     195                 :          33 :           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                 :      359744 :   for (i = 1; !g_atomic_int_get (&data->stopping); i++)
     208                 :             :     {
     209                 :      359743 :       test_closure (data->closure);
     210                 :      359743 :       if (i % 10000 == 0)
     211                 :             :         {
     212                 :          35 :           g_test_message ("Yielding from thread2");
     213                 :          35 :           g_thread_yield (); /* force context switch */
     214                 :          35 :           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 2.0-1