LCOV - code coverage report
Current view: top level - glib/gobject/tests - threadtests.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 208 211 98.6 %
Date: 2024-04-30 05:17:35 Functions: 30 30 100.0 %
Branches: 44 58 75.9 %

           Branch data     Line data    Source code
       1                 :            : /* GLib testing framework examples and tests
       2                 :            :  * Copyright (C) 2008 Imendio AB
       3                 :            :  * Authors: Tim Janik
       4                 :            :  *
       5                 :            :  * SPDX-License-Identifier: LicenseRef-old-glib-tests
       6                 :            :  *
       7                 :            :  * This work is provided "as is"; redistribution and modification
       8                 :            :  * in whole or in part, in any medium, physical or electronic is
       9                 :            :  * permitted without restriction.
      10                 :            :  *
      11                 :            :  * This work is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      14                 :            :  *
      15                 :            :  * In no event shall the authors or contributors be liable for any
      16                 :            :  * direct, indirect, incidental, special, exemplary, or consequential
      17                 :            :  * damages (including, but not limited to, procurement of substitute
      18                 :            :  * goods or services; loss of use, data, or profits; or business
      19                 :            :  * interruption) however caused and on any theory of liability, whether
      20                 :            :  * in contract, strict liability, or tort (including negligence or
      21                 :            :  * otherwise) arising in any way out of the use of this software, even
      22                 :            :  * if advised of the possibility of such damage.
      23                 :            :  */
      24                 :            : 
      25                 :            : #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
      26                 :            : #define GLIB_DISABLE_DEPRECATION_WARNINGS
      27                 :            : #endif
      28                 :            : 
      29                 :            : #include <glib.h>
      30                 :            : #include <glib-object.h>
      31                 :            : 
      32                 :            : static int mtsafe_call_counter = 0; /* multi thread safe call counter, must be accessed atomically */
      33                 :            : static int unsafe_call_counter = 0; /* single-threaded call counter */
      34                 :            : static GCond sync_cond;
      35                 :            : static GMutex sync_mutex;
      36                 :            : 
      37                 :            : #define NUM_COUNTER_INCREMENTS 100000
      38                 :            : 
      39                 :            : static void
      40                 :          6 : call_counter_init (gpointer tclass)
      41                 :            : {
      42                 :            :   int i;
      43         [ +  + ]:     600006 :   for (i = 0; i < NUM_COUNTER_INCREMENTS; i++)
      44                 :            :     {
      45                 :     600000 :       int saved_unsafe_call_counter = unsafe_call_counter;
      46                 :     600000 :       g_atomic_int_add (&mtsafe_call_counter, 1); /* real call count update */
      47                 :     600000 :       g_thread_yield(); /* let concurrent threads corrupt the unsafe_call_counter state */
      48                 :     600000 :       unsafe_call_counter = 1 + saved_unsafe_call_counter; /* non-atomic counter update */
      49                 :            :     }
      50                 :          6 : }
      51                 :            : 
      52                 :          2 : static void interface_per_class_init (void) { call_counter_init (NULL); }
      53                 :            : 
      54                 :            : /* define 3 test interfaces */
      55                 :            : typedef GTypeInterface MyFace0Interface;
      56                 :            : static GType my_face0_get_type (void);
      57   [ +  -  +  -  :          1 : G_DEFINE_INTERFACE (MyFace0, my_face0, G_TYPE_OBJECT)
                   +  - ]
      58                 :          1 : static void my_face0_default_init (MyFace0Interface *iface) { call_counter_init (iface); }
      59                 :            : typedef GTypeInterface MyFace1Interface;
      60                 :            : static GType my_face1_get_type (void);
      61   [ +  -  +  -  :          1 : G_DEFINE_INTERFACE (MyFace1, my_face1, G_TYPE_OBJECT)
                   +  - ]
      62                 :          1 : static void my_face1_default_init (MyFace1Interface *iface) { call_counter_init (iface); }
      63                 :            : 
      64                 :            : /* define 3 test objects, adding interfaces 0 & 1, and adding interface 2 after class initialization */
      65                 :            : typedef GObject         MyTester0;
      66                 :            : typedef GObjectClass    MyTester0Class;
      67                 :            : static GType my_tester0_get_type (void);
      68   [ +  +  +  -  :       5002 : G_DEFINE_TYPE_WITH_CODE (MyTester0, my_tester0, G_TYPE_OBJECT,
                   +  + ]
      69                 :            :                          G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
      70                 :            :                          G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
      71                 :       5000 : static void my_tester0_init (MyTester0*t) {}
      72                 :          1 : static void my_tester0_class_init (MyTester0Class*c) { call_counter_init (c); }
      73                 :            : typedef GObject         MyTester1;
      74                 :            : typedef GObjectClass    MyTester1Class;
      75                 :            : 
      76                 :            : /* Disabled for now (see https://bugzilla.gnome.org/show_bug.cgi?id=687659) */
      77                 :            : #if 0
      78                 :            : typedef GTypeInterface MyFace2Interface;
      79                 :            : static GType my_face2_get_type (void);
      80                 :            : G_DEFINE_INTERFACE (MyFace2, my_face2, G_TYPE_OBJECT)
      81                 :            : static void my_face2_default_init (MyFace2Interface *iface) { call_counter_init (iface); }
      82                 :            : 
      83                 :            : static GType my_tester1_get_type (void);
      84                 :            : G_DEFINE_TYPE_WITH_CODE (MyTester1, my_tester1, G_TYPE_OBJECT,
      85                 :            :                          G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
      86                 :            :                          G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
      87                 :            : static void my_tester1_init (MyTester1*t) {}
      88                 :            : static void my_tester1_class_init (MyTester1Class*c) { call_counter_init (c); }
      89                 :            : typedef GObject         MyTester2;
      90                 :            : typedef GObjectClass    MyTester2Class;
      91                 :            : static GType my_tester2_get_type (void);
      92                 :            : G_DEFINE_TYPE_WITH_CODE (MyTester2, my_tester2, G_TYPE_OBJECT,
      93                 :            :                          G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
      94                 :            :                          G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
      95                 :            : static void my_tester2_init (MyTester2*t) {}
      96                 :            : static void my_tester2_class_init (MyTester2Class*c) { call_counter_init (c); }
      97                 :            : 
      98                 :            : static gpointer
      99                 :            : tester_init_thread (gpointer data)
     100                 :            : {
     101                 :            :   const GInterfaceInfo face2_interface_info = { (GInterfaceInitFunc) interface_per_class_init, NULL, NULL };
     102                 :            :   gpointer klass;
     103                 :            :   /* first, synchronize with other threads,
     104                 :            :    * then run interface and class initializers,
     105                 :            :    * using unsafe_call_counter concurrently
     106                 :            :    */
     107                 :            :   g_mutex_lock (&sync_mutex);
     108                 :            :   g_mutex_unlock (&sync_mutex);
     109                 :            :   /* test default interface initialization for face0 */
     110                 :            :   g_type_default_interface_unref (g_type_default_interface_ref (my_face0_get_type()));
     111                 :            :   /* test class initialization, face0 per-class initializer, face1 default and per-class initializer */
     112                 :            :   klass = g_type_class_ref ((GType) data);
     113                 :            :   /* test face2 default and per-class initializer, after class_init */
     114                 :            :   g_type_add_interface_static (G_TYPE_FROM_CLASS (klass), my_face2_get_type(), &face2_interface_info);
     115                 :            :   /* cleanups */
     116                 :            :   g_type_class_unref (klass);
     117                 :            :   return NULL;
     118                 :            : }
     119                 :            : 
     120                 :            : static void
     121                 :            : test_threaded_class_init (void)
     122                 :            : {
     123                 :            :   GThread *t1, *t2, *t3;
     124                 :            : 
     125                 :            :   /* pause newly created threads */
     126                 :            :   g_mutex_lock (&sync_mutex);
     127                 :            : 
     128                 :            :   /* create threads */
     129                 :            :   t1 = g_thread_create (tester_init_thread, (gpointer) my_tester0_get_type(), TRUE, NULL);
     130                 :            :   t2 = g_thread_create (tester_init_thread, (gpointer) my_tester1_get_type(), TRUE, NULL);
     131                 :            :   t3 = g_thread_create (tester_init_thread, (gpointer) my_tester2_get_type(), TRUE, NULL);
     132                 :            : 
     133                 :            :   /* execute threads */
     134                 :            :   g_mutex_unlock (&sync_mutex);
     135                 :            :   while (g_atomic_int_get (&mtsafe_call_counter) < (3 + 3 + 3 * 3) * NUM_COUNTER_INCREMENTS)
     136                 :            :     {
     137                 :            :       if (g_test_verbose())
     138                 :            :         g_printerr ("Initializers counted: %u\n", g_atomic_int_get (&mtsafe_call_counter));
     139                 :            :       g_usleep (50 * 1000); /* wait for threads to complete */
     140                 :            :     }
     141                 :            :   if (g_test_verbose())
     142                 :            :     g_printerr ("Total initializers: %u\n", g_atomic_int_get (&mtsafe_call_counter));
     143                 :            :   /* ensure non-corrupted counter updates */
     144                 :            :   g_assert_cmpint (g_atomic_int_get (&mtsafe_call_counter), ==, unsafe_call_counter);
     145                 :            : 
     146                 :            :   g_thread_join (t1);
     147                 :            :   g_thread_join (t2);
     148                 :            :   g_thread_join (t3);
     149                 :            : }
     150                 :            : #endif
     151                 :            : 
     152                 :            : typedef struct {
     153                 :            :   GObject parent;
     154                 :            :   char   *name;
     155                 :            : } PropTester;
     156                 :            : typedef GObjectClass    PropTesterClass;
     157                 :            : static GType prop_tester_get_type (void);
     158   [ +  +  +  -  :          4 : G_DEFINE_TYPE (PropTester, prop_tester, G_TYPE_OBJECT)
                   +  + ]
     159                 :            : #define PROP_NAME 1
     160                 :            : static void
     161                 :          2 : prop_tester_init (PropTester* t)
     162                 :            : {
     163                 :          2 :   if (t->name == NULL)
     164                 :            :     { } /* needs unit test framework initialization: g_test_bug ("race initializing properties"); */
     165                 :          2 : }
     166                 :            : static void
     167                 :          2 : prop_tester_set_property (GObject        *object,
     168                 :            :                           guint           property_id,
     169                 :            :                           const GValue   *value,
     170                 :            :                           GParamSpec     *pspec)
     171                 :          2 : {}
     172                 :            : static void
     173                 :          1 : prop_tester_class_init (PropTesterClass *c)
     174                 :            : {
     175                 :            :   int i;
     176                 :            :   GParamSpec *param;
     177                 :          1 :   GObjectClass *gobject_class = G_OBJECT_CLASS (c);
     178                 :            : 
     179                 :          1 :   gobject_class->set_property = prop_tester_set_property; /* silence GObject checks */
     180                 :            : 
     181                 :          1 :   g_mutex_lock (&sync_mutex);
     182                 :          1 :   g_cond_signal (&sync_cond);
     183                 :          1 :   g_mutex_unlock (&sync_mutex);
     184                 :            : 
     185         [ +  + ]:        101 :   for (i = 0; i < 100; i++) /* wait a bit. */
     186                 :        100 :     g_thread_yield();
     187                 :            : 
     188                 :          1 :   call_counter_init (c);
     189                 :          1 :   param = g_param_spec_string ("name", "name_i18n",
     190                 :            :                                "yet-more-wasteful-i18n",
     191                 :            :                                NULL,
     192                 :            :                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
     193                 :            :                                G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB |
     194                 :            :                                G_PARAM_STATIC_NICK);
     195                 :          1 :   g_object_class_install_property (gobject_class, PROP_NAME, param);
     196                 :          1 : }
     197                 :            : 
     198                 :            : static gpointer
     199                 :          2 : object_create (gpointer data)
     200                 :            : {
     201                 :          2 :   GObject *obj = g_object_new (prop_tester_get_type(), "name", "fish", NULL);
     202                 :          2 :   g_object_unref (obj);
     203                 :          2 :   return NULL;
     204                 :            : }
     205                 :            : 
     206                 :            : static void
     207                 :          1 : test_threaded_object_init (void)
     208                 :            : {
     209                 :            :   GThread *creator;
     210                 :          1 :   g_mutex_lock (&sync_mutex);
     211                 :            : 
     212                 :          1 :   creator = g_thread_create (object_create, NULL, TRUE, NULL);
     213                 :            :   /* really provoke the race */
     214                 :          1 :   g_cond_wait (&sync_cond, &sync_mutex);
     215                 :            : 
     216                 :          1 :   object_create (NULL);
     217                 :          1 :   g_mutex_unlock (&sync_mutex);
     218                 :            : 
     219                 :          1 :   g_thread_join (creator);
     220                 :          1 : }
     221                 :            : 
     222                 :            : typedef struct {
     223                 :            :     MyTester0 *strong;
     224                 :            :     guint unref_delay;
     225                 :            : } UnrefInThreadData;
     226                 :            : 
     227                 :            : static gpointer
     228                 :       5000 : unref_in_thread (gpointer p)
     229                 :            : {
     230                 :       5000 :   UnrefInThreadData *data = p;
     231                 :            : 
     232                 :       5000 :   g_usleep (data->unref_delay);
     233                 :       5000 :   g_object_unref (data->strong);
     234                 :            : 
     235                 :       5000 :   return NULL;
     236                 :            : }
     237                 :            : 
     238                 :            : /* undefine to see this test fail without GWeakRef */
     239                 :            : #define HAVE_G_WEAK_REF
     240                 :            : 
     241                 :            : #define SLEEP_MIN_USEC 1
     242                 :            : #define SLEEP_MAX_USEC 10
     243                 :            : 
     244                 :            : static void
     245                 :          1 : test_threaded_weak_ref (void)
     246                 :            : {
     247                 :            :   guint i;
     248                 :          1 :   guint get_wins = 0, unref_wins = 0;
     249                 :            :   guint n;
     250                 :            : 
     251         [ -  + ]:          1 :   if (g_test_thorough ())
     252                 :          0 :     n = NUM_COUNTER_INCREMENTS;
     253                 :            :   else
     254                 :          1 :     n = NUM_COUNTER_INCREMENTS / 20;
     255                 :            : 
     256                 :            : #ifdef G_OS_WIN32
     257                 :            :   /* On Windows usleep has millisecond resolution and gets rounded up
     258                 :            :    * leading to the test running for a long time. */
     259                 :            :   n /= 10;
     260                 :            : #endif
     261                 :            : 
     262         [ +  + ]:       5001 :   for (i = 0; i < n; i++)
     263                 :            :     {
     264                 :            :       UnrefInThreadData data;
     265                 :            : #ifdef HAVE_G_WEAK_REF
     266                 :            :       /* GWeakRef<MyTester0> in C++ terms */
     267                 :            :       GWeakRef weak;
     268                 :            : #else
     269                 :            :       gpointer weak;
     270                 :            : #endif
     271                 :            :       MyTester0 *strengthened;
     272                 :            :       guint get_delay;
     273                 :            :       GThread *thread;
     274                 :       5000 :       GError *error = NULL;
     275                 :            : 
     276   [ -  +  -  - ]:       5000 :       if (g_test_verbose () && (i % (n/20)) == 0)
     277                 :          0 :         g_printerr ("%u%%\n", ((i * 100) / n));
     278                 :            : 
     279                 :            :       /* Have an object and a weak ref to it */
     280                 :       5000 :       data.strong = g_object_new (my_tester0_get_type (), NULL);
     281                 :            : 
     282                 :            : #ifdef HAVE_G_WEAK_REF
     283                 :       5000 :       g_weak_ref_init (&weak, data.strong);
     284                 :            : #else
     285                 :            :       weak = data.strong;
     286                 :            :       g_object_add_weak_pointer ((GObject *) weak, &weak);
     287                 :            : #endif
     288                 :            : 
     289                 :            :       /* Delay for a random time on each side of the race, to perturb the
     290                 :            :        * timing. Ideally, we want each side to win half the races; on
     291                 :            :        * smcv's laptop, these timings are about right.
     292                 :            :        */
     293                 :       5000 :       data.unref_delay = g_random_int_range (SLEEP_MIN_USEC / 2, SLEEP_MAX_USEC / 2);
     294                 :       5000 :       get_delay = g_random_int_range (SLEEP_MIN_USEC, SLEEP_MAX_USEC);
     295                 :            : 
     296                 :            :       /* One half of the race is to unref the shared object */
     297                 :       5000 :       thread = g_thread_create (unref_in_thread, &data, TRUE, &error);
     298                 :       5000 :       g_assert_no_error (error);
     299                 :            : 
     300                 :            :       /* The other half of the race is to get the object from the "global
     301                 :            :        * singleton"
     302                 :            :        */
     303                 :       5000 :       g_usleep (get_delay);
     304                 :            : 
     305                 :            : #ifdef HAVE_G_WEAK_REF
     306                 :       5000 :       strengthened = g_weak_ref_get (&weak);
     307                 :            : #else
     308                 :            :       /* Spot the unsafe pointer access! In GDBusConnection this is rather
     309                 :            :        * better-hidden, but ends up with essentially the same thing, albeit
     310                 :            :        * cleared in dispose() rather than by a traditional weak pointer
     311                 :            :        */
     312                 :            :       strengthened = weak;
     313                 :            : 
     314                 :            :       if (strengthened != NULL)
     315                 :            :         g_object_ref (strengthened);
     316                 :            : #endif
     317                 :            : 
     318         [ +  + ]:       5000 :       if (strengthened != NULL)
     319                 :       4037 :         g_assert (G_IS_OBJECT (strengthened));
     320                 :            : 
     321                 :            :       /* Wait for the thread to run */
     322                 :       5000 :       g_thread_join (thread);
     323                 :            : 
     324         [ +  + ]:       5000 :       if (strengthened != NULL)
     325                 :            :         {
     326                 :       4037 :           get_wins++;
     327                 :       4037 :           g_assert (G_IS_OBJECT (strengthened));
     328                 :       4037 :           g_object_unref (strengthened);
     329                 :            :         }
     330                 :            :       else
     331                 :            :         {
     332                 :        963 :           unref_wins++;
     333                 :            :         }
     334                 :            : 
     335                 :            : #ifdef HAVE_G_WEAK_REF
     336                 :       5000 :       g_weak_ref_clear (&weak);
     337                 :            : #else
     338                 :            :       if (weak != NULL)
     339                 :            :         g_object_remove_weak_pointer (weak, &weak);
     340                 :            : #endif
     341                 :            :     }
     342                 :            : 
     343         [ -  + ]:          1 :   if (g_test_verbose ())
     344                 :          0 :     g_printerr ("Race won by get %u times, unref %u times\n",
     345                 :            :              get_wins, unref_wins);
     346                 :          1 : }
     347                 :            : 
     348                 :            : typedef struct
     349                 :            : {
     350                 :            :   GObject *object;
     351                 :            :   GWeakRef *weak;
     352                 :            :   gint started; /* (atomic) */
     353                 :            :   gint finished; /* (atomic) */
     354                 :            :   gint disposing; /* (atomic) */
     355                 :            : } ThreadedWeakRefData;
     356                 :            : 
     357                 :            : static void
     358                 :          1 : on_weak_ref_disposed (gpointer data,
     359                 :            :                       GObject *gobj)
     360                 :     426486 : {
     361                 :          1 :   ThreadedWeakRefData *thread_data = data;
     362                 :            : 
     363                 :            :   /* Wait until the thread has started */
     364         [ +  + ]:     425935 :   while (!g_atomic_int_get (&thread_data->started))
     365                 :     425934 :     continue;
     366                 :            : 
     367                 :          1 :   g_atomic_int_set (&thread_data->disposing, 1);
     368                 :            : 
     369                 :            :   /* Wait for the thread to act, so that the object is still valid */
     370         [ +  + ]:        553 :   while (!g_atomic_int_get (&thread_data->finished))
     371                 :        552 :     continue;
     372                 :            : 
     373                 :          1 :   g_atomic_int_set (&thread_data->disposing, 0);
     374                 :          1 : }
     375                 :            : 
     376                 :            : static gpointer
     377                 :          1 : on_other_thread_weak_ref (gpointer user_data)
     378                 :          1 : {
     379                 :          1 :   ThreadedWeakRefData *thread_data = user_data;
     380                 :          1 :   GObject *object = thread_data->object;
     381                 :            : 
     382                 :          1 :   g_atomic_int_set (&thread_data->started, 1);
     383                 :            : 
     384                 :            :   /* Ensure we've started disposal */
     385         [ +  + ]:          2 :   while (!g_atomic_int_get (&thread_data->disposing))
     386                 :          1 :     continue;
     387                 :            : 
     388                 :          1 :   g_object_ref (object);
     389                 :          1 :   g_weak_ref_set (thread_data->weak, object);
     390                 :          1 :   g_object_unref (object);
     391                 :            : 
     392                 :          1 :   g_assert_cmpint (thread_data->disposing, ==, 1);
     393                 :          1 :   g_atomic_int_set (&thread_data->finished, 1);
     394                 :            : 
     395                 :          1 :   return NULL;
     396                 :            : }
     397                 :            : 
     398                 :            : static void
     399                 :          1 : test_threaded_weak_ref_finalization (void)
     400                 :            : {
     401                 :          1 :   GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
     402                 :          1 :   GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
     403                 :          1 :   ThreadedWeakRefData thread_data = {
     404                 :            :     .object = obj, .weak = &weak, .started = 0, .finished = 0
     405                 :            :   };
     406                 :            : 
     407                 :          1 :   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
     408                 :          1 :   g_test_summary ("Test that a weak ref added by another thread during dispose "
     409                 :            :                   "of a GObject is cleared during finalisation. "
     410                 :            :                   "Use on_weak_ref_disposed() to synchronize the other thread "
     411                 :            :                   "with the dispose vfunc.");
     412                 :            : 
     413                 :          1 :   g_weak_ref_init (&weak, NULL);
     414                 :          1 :   g_object_weak_ref (obj, on_weak_ref_disposed, &thread_data);
     415                 :            : 
     416                 :          1 :   g_assert_cmpint (obj->ref_count, ==, 1);
     417                 :          1 :   g_thread_unref (g_thread_new ("on_other_thread",
     418                 :            :                                 on_other_thread_weak_ref,
     419                 :            :                                 &thread_data));
     420                 :          1 :   g_object_unref (obj);
     421                 :            : 
     422                 :            :   /* This is what this test is about: at this point the weak reference
     423                 :            :    * should have been unset (and not point to a dead object either). */
     424                 :          1 :   g_assert_null (g_weak_ref_get (&weak));
     425                 :          1 : }
     426                 :            : 
     427                 :            : typedef struct
     428                 :            : {
     429                 :            :   GObject *object;
     430                 :            :   int done;    /* (atomic) */
     431                 :            :   int toggles; /* (atomic) */
     432                 :            : } ToggleNotifyThreadData;
     433                 :            : 
     434                 :            : static gpointer
     435                 :          2 : on_reffer_thread (gpointer user_data)
     436                 :            : {
     437                 :          2 :   ToggleNotifyThreadData *thread_data = user_data;
     438                 :            : 
     439         [ +  + ]:    5747159 :   while (!g_atomic_int_get (&thread_data->done))
     440                 :            :     {
     441                 :    5747157 :       g_object_ref (thread_data->object);
     442                 :    5747157 :       g_object_unref (thread_data->object);
     443                 :            :     }
     444                 :            : 
     445                 :          2 :   return NULL;
     446                 :            : }
     447                 :            : 
     448                 :            : static void
     449                 :      67557 : on_toggle_notify (gpointer data,
     450                 :            :                   GObject *object,
     451                 :            :                   gboolean is_last_ref)
     452                 :            : {
     453                 :            :   /* Anything could be put here, but we don't care for this test.
     454                 :            :    * Actually having this empty made the bug to happen more frequently (being
     455                 :            :    * timing related).
     456                 :            :    */
     457                 :      67557 : }
     458                 :            : 
     459                 :            : static gpointer
     460                 :          1 : on_toggler_thread (gpointer user_data)
     461                 :            : {
     462                 :          1 :   ToggleNotifyThreadData *thread_data = user_data;
     463                 :            : 
     464         [ +  + ]:     100002 :   while (!g_atomic_int_get (&thread_data->done))
     465                 :            :     {
     466                 :     100001 :       g_object_ref (thread_data->object);
     467                 :     100001 :       g_object_remove_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
     468                 :     100001 :       g_object_add_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
     469                 :     100001 :       g_object_unref (thread_data->object);
     470                 :     100001 :       g_atomic_int_add (&thread_data->toggles, 1);
     471                 :            :     }
     472                 :            : 
     473                 :          1 :   return NULL;
     474                 :            : }
     475                 :            : 
     476                 :            : static void
     477                 :          1 : test_threaded_toggle_notify (void)
     478                 :            : {
     479                 :          1 :   GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
     480                 :          1 :   ToggleNotifyThreadData data = { object, FALSE, 0 };
     481                 :            :   GThread *threads[3];
     482                 :            :   gsize i;
     483         [ -  + ]:          1 :   const int n_iterations = g_test_thorough () ? 1000000 : 100000;
     484                 :            : 
     485                 :          1 :   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2394");
     486                 :          1 :   g_test_summary ("Test that toggle reference notifications can be changed "
     487                 :            :                   "safely from another (the main) thread without causing the "
     488                 :            :                   "notifying thread to abort");
     489                 :            : 
     490                 :          1 :   g_object_add_toggle_ref (object, on_toggle_notify, &data);
     491                 :          1 :   g_object_unref (object);
     492                 :            : 
     493                 :          1 :   g_assert_cmpint (object->ref_count, ==, 1);
     494                 :          1 :   threads[0] = g_thread_new ("on_reffer_thread", on_reffer_thread, &data);
     495                 :          1 :   threads[1] = g_thread_new ("on_another_reffer_thread", on_reffer_thread, &data);
     496                 :          1 :   threads[2] = g_thread_new ("on_main_toggler_thread", on_toggler_thread, &data);
     497                 :            : 
     498                 :            :   /* We need to wait here for the threads to run for a bit in order to make the
     499                 :            :    * race to happen, so we wait for an high number of toggle changes to be met
     500                 :            :    * so that we can be consistent on each platform.
     501                 :            :    */
     502         [ +  + ]:    7763321 :   while (g_atomic_int_get (&data.toggles) < n_iterations)
     503                 :            :     ;
     504                 :          1 :   g_atomic_int_set (&data.done, TRUE);
     505                 :            : 
     506         [ +  + ]:          4 :   for (i = 0; i < G_N_ELEMENTS (threads); i++)
     507                 :          3 :     g_thread_join (threads[i]);
     508                 :            : 
     509                 :          1 :   g_assert_cmpint (object->ref_count, ==, 1);
     510                 :          1 :   g_clear_object (&object);
     511                 :          1 : }
     512                 :            : 
     513                 :            : static void
     514                 :          1 : test_threaded_g_pointer_bit_unlock_and_set (void)
     515                 :            : {
     516                 :            :   GObject *obj;
     517                 :            :   gpointer plock;
     518                 :            :   gpointer ptr;
     519                 :            :   guintptr ptr2;
     520                 :            :   gpointer mangled_obj;
     521                 :            : 
     522                 :            : #if defined(__GNUC__)
     523                 :            :   /* We should have at least one bit we can use safely for bit-locking */
     524                 :            :   G_STATIC_ASSERT (__alignof (GObject) > 1);
     525                 :            : #endif
     526                 :            : 
     527                 :          1 :   obj = g_object_new (G_TYPE_OBJECT, NULL);
     528                 :            : 
     529                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0, NULL) == obj);
     530                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, obj) == obj);
     531                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 1, 0, NULL) != obj);
     532                 :            : 
     533                 :          1 :   mangled_obj = obj;
     534                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) == obj);
     535                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x3, mangled_obj) == obj);
     536                 :          1 :   g_atomic_pointer_and (&mangled_obj, ~((gsize) 0x7));
     537                 :          1 :   g_atomic_pointer_or (&mangled_obj, 0x2);
     538                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) != obj);
     539                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj)));
     540                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x3, mangled_obj) == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj)));
     541                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, TRUE, 0x3, mangled_obj) == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj) | ((guintptr) 1)));
     542                 :          1 :   g_atomic_pointer_and (&mangled_obj, ~((gsize) 0x2));
     543                 :          1 :   g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) == obj);
     544                 :          1 :   g_atomic_pointer_or (&mangled_obj, 0x2);
     545                 :            : 
     546                 :          1 :   plock = obj;
     547                 :          1 :   g_pointer_bit_lock (&plock, 0);
     548                 :          1 :   g_assert_true (plock != obj);
     549                 :          1 :   g_pointer_bit_unlock_and_set (&plock, 0, obj, 0);
     550                 :          1 :   g_assert_true (plock == obj);
     551                 :            : 
     552                 :          1 :   plock = obj;
     553                 :          1 :   g_pointer_bit_lock_and_get (&plock, 0, &ptr2);
     554                 :          1 :   g_assert_true ((gpointer) ptr2 == plock);
     555                 :          1 :   g_assert_true (plock != obj);
     556                 :          1 :   g_atomic_pointer_set (&plock, mangled_obj);
     557                 :          1 :   g_pointer_bit_unlock_and_set (&plock, 0, obj, 0);
     558                 :          1 :   g_assert_true (plock == obj);
     559                 :            : 
     560                 :          1 :   plock = obj;
     561                 :          1 :   g_pointer_bit_lock_and_get (&plock, 0, NULL);
     562                 :          1 :   g_assert_true (plock != obj);
     563                 :          1 :   g_atomic_pointer_set (&plock, mangled_obj);
     564                 :          1 :   g_pointer_bit_unlock_and_set (&plock, 0, obj, 0x7);
     565                 :          1 :   g_assert_true (plock != obj);
     566                 :          1 :   g_assert_true (plock == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj)));
     567                 :            : 
     568                 :          1 :   plock = NULL;
     569                 :          1 :   g_pointer_bit_lock (&plock, 0);
     570                 :          1 :   g_assert_true (plock != NULL);
     571                 :          1 :   g_pointer_bit_unlock_and_set (&plock, 0, NULL, 0);
     572                 :          1 :   g_assert_true (plock == NULL);
     573                 :            : 
     574                 :          1 :   ptr = ((char *) obj) + 1;
     575                 :          1 :   plock = obj;
     576                 :          1 :   g_pointer_bit_lock (&plock, 0);
     577                 :          1 :   g_assert_true (plock == ptr);
     578                 :          1 :   g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL,
     579                 :            :                          "*assertion 'ptr == pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, 0, NULL)' failed*");
     580                 :          1 :   g_pointer_bit_unlock_and_set (&plock, 0, ptr, 0);
     581                 :          1 :   g_test_assert_expected_messages ();
     582                 :          1 :   g_assert_true (plock != ptr);
     583                 :          1 :   g_assert_true (plock == obj);
     584                 :            : 
     585                 :          1 :   g_object_unref (obj);
     586                 :          1 : }
     587                 :            : 
     588                 :            : int
     589                 :          1 : main (int   argc,
     590                 :            :       char *argv[])
     591                 :            : {
     592                 :          1 :   g_test_init (&argc, &argv, NULL);
     593                 :            : 
     594                 :            :   /* g_test_add_func ("/GObject/threaded-class-init", test_threaded_class_init); */
     595                 :          1 :   g_test_add_func ("/GObject/threaded-object-init", test_threaded_object_init);
     596                 :          1 :   g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref);
     597                 :          1 :   g_test_add_func ("/GObject/threaded-weak-ref/on-finalization",
     598                 :            :                    test_threaded_weak_ref_finalization);
     599                 :          1 :   g_test_add_func ("/GObject/threaded-toggle-notify",
     600                 :            :                    test_threaded_toggle_notify);
     601                 :          1 :   g_test_add_func ("/GObject/threaded-g-pointer-bit-unlock-and-set",
     602                 :            :                    test_threaded_g_pointer_bit_unlock_and_set);
     603                 :            : 
     604                 :          1 :   return g_test_run();
     605                 :            : }

Generated by: LCOV version 1.14