LCOV - code coverage report
Current view: top level - gio/tests - cancellable.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 75.2 % 395 297
Test Date: 2024-11-26 05:23:01 Functions: 74.2 % 31 23
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* GIO - GLib Input, Output and Streaming Library
       2                 :             :  *
       3                 :             :  * Copyright (C) 2011 Collabora Ltd.
       4                 :             :  *
       5                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :             :  *
       7                 :             :  * This library is free software; you can redistribute it and/or
       8                 :             :  * modify it under the terms of the GNU Lesser General Public
       9                 :             :  * License as published by the Free Software Foundation; either
      10                 :             :  * version 2.1 of the License, or (at your option) any later version.
      11                 :             :  *
      12                 :             :  * This library is distributed in the hope that it will be useful,
      13                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :             :  * Lesser General Public License for more details.
      16                 :             :  *
      17                 :             :  * You should have received a copy of the GNU Lesser General
      18                 :             :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19                 :             :  *
      20                 :             :  * Author: Stef Walter <stefw@collabora.co.uk>
      21                 :             :  */
      22                 :             : 
      23                 :             : #include <locale.h>
      24                 :             : 
      25                 :             : #include <gio/gio.h>
      26                 :             : 
      27                 :             : #include "glib/glib-private.h"
      28                 :             : 
      29                 :             : /* How long to wait in ms for each iteration */
      30                 :             : #define WAIT_ITERATION (10)
      31                 :             : 
      32                 :             : static gint num_async_operations = 0;
      33                 :             : 
      34                 :             : typedef struct
      35                 :             : {
      36                 :             :   guint iterations_requested;  /* construct-only */
      37                 :             :   guint iterations_done;  /* (atomic) */
      38                 :             : } MockOperationData;
      39                 :             : 
      40                 :             : static void
      41                 :           0 : mock_operation_free (gpointer user_data)
      42                 :             : {
      43                 :           0 :   MockOperationData *data = user_data;
      44                 :           0 :   g_free (data);
      45                 :           0 : }
      46                 :             : 
      47                 :             : static void
      48                 :           0 : mock_operation_thread (GTask        *task,
      49                 :             :                        gpointer      source_object,
      50                 :             :                        gpointer      task_data,
      51                 :             :                        GCancellable *cancellable)
      52                 :             : {
      53                 :           0 :   MockOperationData *data = task_data;
      54                 :             :   guint i;
      55                 :             : 
      56                 :           0 :   for (i = 0; i < data->iterations_requested; i++)
      57                 :             :     {
      58                 :           0 :       if (g_cancellable_is_cancelled (cancellable))
      59                 :           0 :         break;
      60                 :           0 :       if (g_test_verbose ())
      61                 :           0 :         g_test_message ("THRD: %u iteration %u", data->iterations_requested, i);
      62                 :           0 :       g_usleep (WAIT_ITERATION * 1000);
      63                 :             :     }
      64                 :             : 
      65                 :           0 :   if (g_test_verbose ())
      66                 :           0 :     g_test_message ("THRD: %u stopped at %u", data->iterations_requested, i);
      67                 :           0 :   g_atomic_int_add (&data->iterations_done, i);
      68                 :             : 
      69                 :           0 :   g_task_return_boolean (task, TRUE);
      70                 :           0 : }
      71                 :             : 
      72                 :             : static gboolean
      73                 :           0 : mock_operation_timeout (gpointer user_data)
      74                 :             : {
      75                 :             :   GTask *task;
      76                 :             :   MockOperationData *data;
      77                 :           0 :   gboolean done = FALSE;
      78                 :             :   guint iterations_done;
      79                 :             : 
      80                 :           0 :   task = G_TASK (user_data);
      81                 :           0 :   data = g_task_get_task_data (task);
      82                 :           0 :   iterations_done = g_atomic_int_get (&data->iterations_done);
      83                 :             : 
      84                 :           0 :   if (iterations_done >= data->iterations_requested)
      85                 :           0 :       done = TRUE;
      86                 :             : 
      87                 :           0 :   if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
      88                 :           0 :       done = TRUE;
      89                 :             : 
      90                 :           0 :   if (done)
      91                 :             :     {
      92                 :           0 :       if (g_test_verbose ())
      93                 :           0 :         g_test_message ("LOOP: %u stopped at %u",
      94                 :             :                         data->iterations_requested, iterations_done);
      95                 :           0 :       g_task_return_boolean (task, TRUE);
      96                 :           0 :       return G_SOURCE_REMOVE;
      97                 :             :     }
      98                 :             :   else
      99                 :             :     {
     100                 :           0 :       g_atomic_int_inc (&data->iterations_done);
     101                 :           0 :       if (g_test_verbose ())
     102                 :           0 :         g_test_message ("LOOP: %u iteration %u",
     103                 :             :                         data->iterations_requested, iterations_done + 1);
     104                 :           0 :       return G_SOURCE_CONTINUE;
     105                 :             :     }
     106                 :             : }
     107                 :             : 
     108                 :             : static void
     109                 :           0 : mock_operation_async (guint                wait_iterations,
     110                 :             :                       gboolean             run_in_thread,
     111                 :             :                       GCancellable        *cancellable,
     112                 :             :                       GAsyncReadyCallback  callback,
     113                 :             :                       gpointer             user_data)
     114                 :             : {
     115                 :             :   GTask *task;
     116                 :             :   MockOperationData *data;
     117                 :             : 
     118                 :           0 :   task = g_task_new (NULL, cancellable, callback, user_data);
     119                 :           0 :   data = g_new0 (MockOperationData, 1);
     120                 :           0 :   data->iterations_requested = wait_iterations;
     121                 :           0 :   g_task_set_task_data (task, data, mock_operation_free);
     122                 :             : 
     123                 :           0 :   if (run_in_thread)
     124                 :             :     {
     125                 :           0 :       g_task_run_in_thread (task, mock_operation_thread);
     126                 :           0 :       if (g_test_verbose ())
     127                 :           0 :         g_test_message ("THRD: %d started", wait_iterations);
     128                 :             :     }
     129                 :             :   else
     130                 :             :     {
     131                 :           0 :       g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
     132                 :             :                           g_object_ref (task), g_object_unref);
     133                 :           0 :       if (g_test_verbose ())
     134                 :           0 :         g_test_message ("LOOP: %d started", wait_iterations);
     135                 :             :     }
     136                 :             : 
     137                 :           0 :   g_object_unref (task);
     138                 :           0 : }
     139                 :             : 
     140                 :             : static guint
     141                 :           0 : mock_operation_finish (GAsyncResult  *result,
     142                 :             :                        GError       **error)
     143                 :             : {
     144                 :             :   MockOperationData *data;
     145                 :             :   GTask *task;
     146                 :             : 
     147                 :           0 :   g_assert_true (g_task_is_valid (result, NULL));
     148                 :             : 
     149                 :             :   /* This test expects the return value to be iterations_done even
     150                 :             :    * when an error is set.
     151                 :             :    */
     152                 :           0 :   task = G_TASK (result);
     153                 :           0 :   data = g_task_get_task_data (task);
     154                 :             : 
     155                 :           0 :   g_task_propagate_boolean (task, error);
     156                 :           0 :   return g_atomic_int_get (&data->iterations_done);
     157                 :             : }
     158                 :             : 
     159                 :             : static void
     160                 :           0 : on_mock_operation_ready (GObject      *source,
     161                 :             :                          GAsyncResult *result,
     162                 :             :                          gpointer      user_data)
     163                 :             : {
     164                 :             :   guint iterations_requested;
     165                 :             :   guint iterations_done;
     166                 :           0 :   GError *error = NULL;
     167                 :             : 
     168                 :           0 :   iterations_requested = GPOINTER_TO_UINT (user_data);
     169                 :           0 :   iterations_done = mock_operation_finish (result, &error);
     170                 :             : 
     171                 :           0 :   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
     172                 :           0 :   g_error_free (error);
     173                 :             : 
     174                 :           0 :   g_assert_cmpint (iterations_requested, >, iterations_done);
     175                 :           0 :   num_async_operations--;
     176                 :           0 :   g_main_context_wakeup (NULL);
     177                 :           0 : }
     178                 :             : 
     179                 :             : static void
     180                 :           1 : test_cancel_multiple_concurrent (void)
     181                 :             : {
     182                 :             :   GCancellable *cancellable;
     183                 :             :   guint i, iterations;
     184                 :             : 
     185                 :           1 :   if (!g_test_thorough ())
     186                 :             :     {
     187                 :           1 :       g_test_skip ("Not running timing heavy test");
     188                 :           1 :       return;
     189                 :             :     }
     190                 :             : 
     191                 :           0 :   cancellable = g_cancellable_new ();
     192                 :             : 
     193                 :           0 :   for (i = 0; i < 45; i++)
     194                 :             :     {
     195                 :           0 :       iterations = i + 10;
     196                 :           0 :       mock_operation_async (iterations, g_random_boolean (), cancellable,
     197                 :           0 :                             on_mock_operation_ready, GUINT_TO_POINTER (iterations));
     198                 :           0 :       num_async_operations++;
     199                 :             :     }
     200                 :             : 
     201                 :             :   /* Wait for the threads to start up */
     202                 :           0 :   while (num_async_operations != 45)
     203                 :           0 :     g_main_context_iteration (NULL, TRUE);
     204                 :           0 :   g_assert_cmpint (num_async_operations, ==, 45);\
     205                 :             : 
     206                 :           0 :   if (g_test_verbose ())
     207                 :           0 :     g_test_message ("CANCEL: %d operations", num_async_operations);
     208                 :           0 :   g_cancellable_cancel (cancellable);
     209                 :           0 :   g_assert_true (g_cancellable_is_cancelled (cancellable));
     210                 :             : 
     211                 :             :   /* Wait for all operations to be cancelled */
     212                 :           0 :   while (num_async_operations != 0)
     213                 :           0 :     g_main_context_iteration (NULL, TRUE);
     214                 :           0 :   g_assert_cmpint (num_async_operations, ==, 0);
     215                 :             : 
     216                 :           0 :   g_object_unref (cancellable);
     217                 :             : }
     218                 :             : 
     219                 :             : static void
     220                 :           1 : test_cancel_null (void)
     221                 :             : {
     222                 :           1 :   g_cancellable_cancel (NULL);
     223                 :           1 : }
     224                 :             : 
     225                 :             : typedef struct
     226                 :             : {
     227                 :             :   GCond cond;
     228                 :             :   GMutex mutex;
     229                 :             :   gboolean thread_ready;
     230                 :             :   GAsyncQueue *cancellable_source_queue;  /* (owned) (element-type GCancellableSource) */
     231                 :             : } ThreadedDisposeData;
     232                 :             : 
     233                 :             : static gboolean
     234                 :           0 : cancelled_cb (GCancellable *cancellable,
     235                 :             :               gpointer      user_data)
     236                 :             : {
     237                 :             :   /* Nothing needs to be done here. */
     238                 :           0 :   return G_SOURCE_CONTINUE;
     239                 :             : }
     240                 :             : 
     241                 :             : static gpointer
     242                 :           1 : threaded_dispose_thread_cb (gpointer user_data)
     243                 :             : {
     244                 :           1 :   ThreadedDisposeData *data = user_data;
     245                 :             :   GSource *cancellable_source;
     246                 :             : 
     247                 :           1 :   g_mutex_lock (&data->mutex);
     248                 :           1 :   data->thread_ready = TRUE;
     249                 :           1 :   g_cond_broadcast (&data->cond);
     250                 :           1 :   g_mutex_unlock (&data->mutex);
     251                 :             : 
     252                 :      100001 :   while ((cancellable_source = g_async_queue_pop (data->cancellable_source_queue)) != (gpointer) 1)
     253                 :             :     {
     254                 :             :       /* Race with cancellation of the cancellable. */
     255                 :      100000 :       g_source_unref (cancellable_source);
     256                 :             :     }
     257                 :             : 
     258                 :           1 :   return NULL;
     259                 :             : }
     260                 :             : 
     261                 :             : static void
     262                 :           1 : test_cancellable_source_threaded_dispose (void)
     263                 :             : {
     264                 :             :   ThreadedDisposeData data;
     265                 :           1 :   GThread *thread = NULL;
     266                 :             :   guint i;
     267                 :           1 :   GPtrArray *cancellables_pending_unref = g_ptr_array_new_with_free_func (g_object_unref);
     268                 :             : 
     269                 :           1 :   g_test_summary ("Test a thread race between disposing of a GCancellableSource "
     270                 :             :                   "(in one thread) and cancelling the GCancellable it refers "
     271                 :             :                   "to (in another thread)");
     272                 :           1 :   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1841");
     273                 :             : #ifdef _GLIB_ADDRESS_SANITIZER
     274                 :             :   g_test_message ("We also ensure that no GCancellableSource are leaked");
     275                 :             :   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2309");
     276                 :             : #endif
     277                 :             : 
     278                 :             :   /* Create a new thread and wait until it’s ready to execute. Each iteration of
     279                 :             :    * the test will pass it a new #GCancellableSource. */
     280                 :           1 :   g_cond_init (&data.cond);
     281                 :           1 :   g_mutex_init (&data.mutex);
     282                 :           1 :   data.cancellable_source_queue = g_async_queue_new_full ((GDestroyNotify) g_source_unref);
     283                 :           1 :   data.thread_ready = FALSE;
     284                 :             : 
     285                 :           1 :   g_mutex_lock (&data.mutex);
     286                 :           1 :   thread = g_thread_new ("/cancellable-source/threaded-dispose",
     287                 :             :                          threaded_dispose_thread_cb, &data);
     288                 :             : 
     289                 :           2 :   while (!data.thread_ready)
     290                 :           1 :     g_cond_wait (&data.cond, &data.mutex);
     291                 :           1 :   g_mutex_unlock (&data.mutex);
     292                 :             : 
     293                 :      100001 :   for (i = 0; i < 100000; i++)
     294                 :             :     {
     295                 :      100000 :       GCancellable *cancellable = NULL;
     296                 :      100000 :       GSource *cancellable_source = NULL;
     297                 :             : 
     298                 :             :       /* Create a cancellable and a cancellable source for it. For this test,
     299                 :             :        * there’s no need to attach the source to a #GMainContext. */
     300                 :      100000 :       cancellable = g_cancellable_new ();
     301                 :      100000 :       cancellable_source = g_cancellable_source_new (cancellable);
     302                 :      100000 :       g_source_set_callback (cancellable_source, G_SOURCE_FUNC (cancelled_cb), NULL, NULL);
     303                 :             : 
     304                 :             :       /* Send it to the thread and wait until it’s ready to execute before
     305                 :             :        * cancelling our cancellable. */
     306                 :      100000 :       g_async_queue_push (data.cancellable_source_queue, g_steal_pointer (&cancellable_source));
     307                 :             : 
     308                 :             :       /* Race with disposal of the cancellable source. */
     309                 :      100000 :       g_cancellable_cancel (cancellable);
     310                 :             : 
     311                 :             :       /* This thread can’t drop its reference to the #GCancellable here, as it
     312                 :             :        * might not be the final reference (depending on how the race is
     313                 :             :        * resolved: #GCancellableSource holds a strong ref on the #GCancellable),
     314                 :             :        * and at this point we can’t guarantee to support disposing of a
     315                 :             :        * #GCancellable in a different thread from where it’s created, especially
     316                 :             :        * when signal handlers are connected to it.
     317                 :             :        *
     318                 :             :        * So this is a workaround for a disposal-in-another-thread bug for
     319                 :             :        * #GCancellable, but there’s no hope of debugging and resolving it with
     320                 :             :        * this test setup, and the bug is orthogonal to what’s being tested here
     321                 :             :        * (a race between #GCancellable and #GCancellableSource). */
     322                 :      100000 :       g_ptr_array_add (cancellables_pending_unref, g_steal_pointer (&cancellable));
     323                 :             :     }
     324                 :             : 
     325                 :             :   /* Indicate that the test has finished. Can’t use %NULL as #GAsyncQueue
     326                 :             :    * doesn’t allow that.*/
     327                 :           1 :   g_async_queue_push (data.cancellable_source_queue, (gpointer) 1);
     328                 :             : 
     329                 :           1 :   g_thread_join (g_steal_pointer (&thread));
     330                 :             : 
     331                 :           1 :   g_assert (g_async_queue_length (data.cancellable_source_queue) == 0);
     332                 :           1 :   g_async_queue_unref (data.cancellable_source_queue);
     333                 :           1 :   g_mutex_clear (&data.mutex);
     334                 :           1 :   g_cond_clear (&data.cond);
     335                 :             : 
     336                 :           1 :   g_ptr_array_unref (cancellables_pending_unref);
     337                 :           1 : }
     338                 :             : 
     339                 :             : static void
     340                 :           1 : test_cancellable_poll_fd (void)
     341                 :             : {
     342                 :             :   GCancellable *cancellable;
     343                 :           1 :   GPollFD pollfd = {.fd = -1};
     344                 :           1 :   int fd = -1;
     345                 :             : 
     346                 :             : #ifdef G_OS_WIN32
     347                 :             :   g_test_skip ("Platform not supported");
     348                 :             :   return;
     349                 :             : #endif
     350                 :             : 
     351                 :           1 :   cancellable = g_cancellable_new ();
     352                 :             : 
     353                 :           1 :   g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd));
     354                 :           1 :   g_assert_cmpint (pollfd.fd, >, 0);
     355                 :             : 
     356                 :           1 :   fd = g_cancellable_get_fd (cancellable);
     357                 :           1 :   g_assert_cmpint (fd, >, 0);
     358                 :             : 
     359                 :           1 :   g_cancellable_release_fd (cancellable);
     360                 :           1 :   g_cancellable_release_fd (cancellable);
     361                 :             : 
     362                 :           1 :   g_object_unref (cancellable);
     363                 :           1 : }
     364                 :             : 
     365                 :             : static void
     366                 :           1 : test_cancellable_cancelled_poll_fd (void)
     367                 :             : {
     368                 :             :   GCancellable *cancellable;
     369                 :             :   GPollFD pollfd;
     370                 :             : 
     371                 :             : #ifdef G_OS_WIN32
     372                 :             :   g_test_skip ("Platform not supported");
     373                 :             :   return;
     374                 :             : #endif
     375                 :             : 
     376                 :           1 :   g_test_summary ("Tests that cancellation wakes up a pollable FD on creation");
     377                 :             : 
     378                 :           1 :   cancellable = g_cancellable_new ();
     379                 :           1 :   g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd));
     380                 :           1 :   g_cancellable_cancel (cancellable);
     381                 :             : 
     382                 :           1 :   g_poll (&pollfd, 1, -1);
     383                 :             : 
     384                 :           1 :   g_cancellable_release_fd (cancellable);
     385                 :           1 :   g_object_unref (cancellable);
     386                 :           1 : }
     387                 :             : 
     388                 :             : typedef struct {
     389                 :             :   GCancellable *cancellable;
     390                 :             :   gboolean polling_started; /* Atomic */
     391                 :             : } CancellablePollThreadData;
     392                 :             : 
     393                 :             : static gpointer
     394                 :           1 : cancel_cancellable_thread (gpointer user_data)
     395                 :             : {
     396                 :           1 :   CancellablePollThreadData *thread_data = user_data;
     397                 :             : 
     398                 :           1 :   while (!g_atomic_int_get (&thread_data->polling_started))
     399                 :             :     ;
     400                 :             : 
     401                 :             :   /* Let's just wait a moment before cancelling, this is not really needed
     402                 :             :    * but we do it to simulate that the thread is actually doing something.
     403                 :             :    */
     404                 :           1 :   g_usleep (G_USEC_PER_SEC / 10);
     405                 :           1 :   g_cancellable_cancel (thread_data->cancellable);
     406                 :             : 
     407                 :           1 :   return NULL;
     408                 :             : }
     409                 :             : 
     410                 :             : static gpointer
     411                 :           1 : polling_cancelled_cancellable_thread (gpointer user_data)
     412                 :             : {
     413                 :           1 :   CancellablePollThreadData *thread_data = user_data;
     414                 :             :   GPollFD pollfd;
     415                 :             : 
     416                 :           1 :   g_assert_true (g_cancellable_make_pollfd (thread_data->cancellable, &pollfd));
     417                 :           1 :   g_atomic_int_set (&thread_data->polling_started, TRUE);
     418                 :             : 
     419                 :           1 :   g_poll (&pollfd, 1, -1);
     420                 :             : 
     421                 :           1 :   g_cancellable_release_fd (thread_data->cancellable);
     422                 :             : 
     423                 :           1 :   return NULL;
     424                 :             : }
     425                 :             : 
     426                 :             : static void
     427                 :           1 : test_cancellable_cancelled_poll_fd_threaded (void)
     428                 :             : {
     429                 :             :   GCancellable *cancellable;
     430                 :           1 :   CancellablePollThreadData thread_data = {0};
     431                 :           1 :   GThread *polling_thread = NULL;
     432                 :           1 :   GThread *cancelling_thread = NULL;
     433                 :             :   GPollFD pollfd;
     434                 :             : 
     435                 :             : #ifdef G_OS_WIN32
     436                 :             :   g_test_skip ("Platform not supported");
     437                 :             :   return;
     438                 :             : #endif
     439                 :             : 
     440                 :           1 :   g_test_summary ("Tests that a cancellation wakes up a pollable FD");
     441                 :             : 
     442                 :           1 :   cancellable = g_cancellable_new ();
     443                 :           1 :   g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd));
     444                 :             : 
     445                 :           1 :   thread_data.cancellable = cancellable;
     446                 :             : 
     447                 :           1 :   polling_thread = g_thread_new ("/cancellable/poll-fd-cancelled-threaded/polling",
     448                 :             :                                  polling_cancelled_cancellable_thread,
     449                 :             :                                  &thread_data);
     450                 :           1 :   cancelling_thread = g_thread_new ("/cancellable/poll-fd-cancelled-threaded/cancelling",
     451                 :             :                                     cancel_cancellable_thread, &thread_data);
     452                 :             : 
     453                 :           1 :   g_poll (&pollfd, 1, -1);
     454                 :           1 :   g_assert_true (g_cancellable_is_cancelled (cancellable));
     455                 :           1 :   g_cancellable_release_fd (cancellable);
     456                 :             : 
     457                 :           1 :   g_thread_join (g_steal_pointer (&cancelling_thread));
     458                 :           1 :   g_thread_join (g_steal_pointer (&polling_thread));
     459                 :             : 
     460                 :           1 :   g_object_unref (cancellable);
     461                 :           1 : }
     462                 :             : 
     463                 :             : typedef struct {
     464                 :             :   GMainLoop *loop;
     465                 :             :   GCancellable *cancellable;
     466                 :             :   GCallback callback;
     467                 :             :   gboolean is_disconnecting;
     468                 :             :   gboolean is_resetting;
     469                 :             :   gpointer handler_id;
     470                 :             : } ConnectingThreadData;
     471                 :             : 
     472                 :             : static void
     473                 :           1 : on_cancellable_connect_disconnect (GCancellable *cancellable,
     474                 :             :                                    ConnectingThreadData *data)
     475                 :             : {
     476                 :           1 :   gulong handler_id = (gulong) (guintptr) g_atomic_pointer_exchange (&data->handler_id, 0);
     477                 :           1 :   g_atomic_int_set (&data->is_disconnecting, TRUE);
     478                 :           1 :   g_cancellable_disconnect (cancellable, handler_id);
     479                 :           0 :   g_atomic_int_set (&data->is_disconnecting, FALSE);
     480                 :           0 : }
     481                 :             : 
     482                 :             : static gpointer
     483                 :           2 : connecting_thread (gpointer user_data)
     484                 :             : {
     485                 :             :   GMainContext *context;
     486                 :           2 :   ConnectingThreadData *data = user_data;
     487                 :             :   gulong handler_id;
     488                 :             :   GMainLoop *loop;
     489                 :             : 
     490                 :             :   handler_id =
     491                 :           2 :     g_cancellable_connect (data->cancellable, data->callback, data, NULL);
     492                 :             : 
     493                 :           2 :   context = g_main_context_new ();
     494                 :           2 :   g_main_context_push_thread_default (context);
     495                 :           2 :   loop = g_main_loop_new (context, FALSE);
     496                 :             : 
     497                 :           2 :   g_atomic_pointer_set (&data->handler_id, (gpointer) (guintptr) handler_id);
     498                 :           2 :   g_atomic_pointer_set (&data->loop, loop);
     499                 :           2 :   g_main_loop_run (loop);
     500                 :             : 
     501                 :           2 :   g_main_context_pop_thread_default (context);
     502                 :           2 :   g_main_context_unref (context);
     503                 :           2 :   g_main_loop_unref (loop);
     504                 :             : 
     505                 :           2 :   return NULL;
     506                 :             : }
     507                 :             : 
     508                 :             : static void
     509                 :           2 : test_cancellable_disconnect_on_cancelled_callback_hangs (void)
     510                 :             : {
     511                 :             :   GCancellable *cancellable;
     512                 :           2 :   GThread *thread = NULL;
     513                 :           2 :   GThread *cancelling_thread = NULL;
     514                 :           2 :   ConnectingThreadData thread_data = {0};
     515                 :             :   GMainLoop *thread_loop;
     516                 :             :   gpointer waited;
     517                 :             : 
     518                 :             :   /* While this is not convenient, it's done to ensure that we don't have a
     519                 :             :    * race when trying to cancelling a cancellable that is about to be cancelled
     520                 :             :    * in another thread
     521                 :             :    */
     522                 :           2 :   g_test_summary ("Tests that trying to disconnect a cancellable from the "
     523                 :             :                   "cancelled signal callback will result in a deadlock "
     524                 :             :                   "as per #GCancellable::cancelled");
     525                 :             : 
     526                 :           2 :   if (!g_test_undefined ())
     527                 :             :     {
     528                 :           0 :       g_test_skip ("Skipping testing disallowed behaviour of disconnecting from "
     529                 :             :                   "a cancellable from its cancelled callback");
     530                 :           1 :       return;
     531                 :             :     }
     532                 :             : 
     533                 :             :   /* Run the test in a subprocess. While we can get away with deadlocking a
     534                 :             :    * specific thread on Linux, the libc on FreeBSD manages to detect the
     535                 :             :    * deadlock and aborts the whole test process. */
     536                 :           2 :   if (!g_test_subprocess ())
     537                 :             :     {
     538                 :           1 :       g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
     539                 :           1 :       if (!g_test_trap_has_passed ())
     540                 :           0 :         g_test_trap_assert_stderr ("*Unexpected error from C library during 'pthread_mutex_lock': Resource deadlock avoided.  Aborting.*");
     541                 :           1 :       return;
     542                 :             :     }
     543                 :             : 
     544                 :           1 :   cancellable = g_cancellable_new ();
     545                 :           1 :   thread_data.cancellable = cancellable;
     546                 :           1 :   thread_data.callback = G_CALLBACK (on_cancellable_connect_disconnect);
     547                 :             : 
     548                 :           1 :   g_assert_false (g_atomic_int_get (&thread_data.is_disconnecting));
     549                 :           1 :   g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
     550                 :             : 
     551                 :           1 :   thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs",
     552                 :             :                          connecting_thread, &thread_data);
     553                 :             : 
     554                 :      817588 :   while (!g_atomic_pointer_get (&thread_data.loop))
     555                 :             :     ;
     556                 :             : 
     557                 :           1 :   thread_loop = thread_data.loop;
     558                 :           1 :   g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), !=, 0);
     559                 :             : 
     560                 :             :   /* This thread will hang (at least that's what this test wants to ensure), but
     561                 :             :    * we can't stop it from the caller, unless we'll expose pthread_cancel() (and
     562                 :             :    * similar) to GLib. So it will keep hanging until the test subprocess exits.
     563                 :             :    */
     564                 :           1 :   cancelling_thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs",
     565                 :             :                                     (GThreadFunc) g_cancellable_cancel,
     566                 :             :                                     cancellable);
     567                 :             : 
     568                 :       87687 :   while (!g_cancellable_is_cancelled (cancellable) ||
     569                 :           1 :          !g_atomic_int_get (&thread_data.is_disconnecting))
     570                 :             :     ;
     571                 :             : 
     572                 :           1 :   g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting));
     573                 :           1 :   g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
     574                 :             : 
     575                 :           1 :   waited = &waited;
     576                 :           1 :   g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited);
     577                 :           2 :   while (waited != NULL)
     578                 :           1 :     g_main_context_iteration (NULL, TRUE);
     579                 :             : 
     580                 :           1 :   g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting));
     581                 :             : 
     582                 :           1 :   g_main_loop_quit (thread_loop);
     583                 :           1 :   g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting));
     584                 :             : 
     585                 :           1 :   g_thread_join (g_steal_pointer (&thread));
     586                 :           1 :   g_thread_unref (cancelling_thread);
     587                 :           1 :   g_object_unref (cancellable);
     588                 :             : }
     589                 :             : 
     590                 :             : static void
     591                 :           1 : on_cancelled_reset (GCancellable *cancellable,
     592                 :             :                     gpointer data)
     593                 :             : {
     594                 :           1 :   ConnectingThreadData *thread_data = data;
     595                 :             : 
     596                 :           1 :   g_assert_true (g_cancellable_is_cancelled (cancellable));
     597                 :           1 :   g_atomic_int_set (&thread_data->is_resetting, TRUE);
     598                 :           1 :   g_cancellable_reset (cancellable);
     599                 :           0 :   g_assert_false (g_cancellable_is_cancelled (cancellable));
     600                 :           0 :   g_atomic_int_set (&thread_data->is_resetting, TRUE);
     601                 :           0 : }
     602                 :             : 
     603                 :             : static void
     604                 :           2 : test_cancellable_reset_on_cancelled_callback_hangs (void)
     605                 :             : {
     606                 :             :   GCancellable *cancellable;
     607                 :           2 :   GThread *thread = NULL;
     608                 :           2 :   GThread *cancelling_thread = NULL;
     609                 :           2 :   ConnectingThreadData thread_data = {0};
     610                 :             :   GMainLoop *thread_loop;
     611                 :             :   gpointer waited;
     612                 :             : 
     613                 :             :   /* While this is not convenient, it's done to ensure that we don't have a
     614                 :             :    * race when trying to cancelling a cancellable that is about to be cancelled
     615                 :             :    * in another thread
     616                 :             :    */
     617                 :           2 :   g_test_summary ("Tests that trying to reset a cancellable from the "
     618                 :             :                   "cancelled signal callback will result in a deadlock "
     619                 :             :                   "as per #GCancellable::cancelled");
     620                 :             : 
     621                 :           2 :   if (!g_test_undefined ())
     622                 :             :     {
     623                 :           0 :       g_test_skip ("Skipping testing disallowed behaviour of resetting a "
     624                 :             :                   "cancellable from its callback");
     625                 :           1 :       return;
     626                 :             :     }
     627                 :             : 
     628                 :             :   /* Run the test in a subprocess. While we can get away with deadlocking a
     629                 :             :    * specific thread on Linux, the libc on FreeBSD manages to detect the
     630                 :             :    * deadlock and aborts the whole test process. */
     631                 :           2 :   if (!g_test_subprocess ())
     632                 :             :     {
     633                 :           1 :       g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
     634                 :           1 :       if (!g_test_trap_has_passed ())
     635                 :           0 :         g_test_trap_assert_stderr ("*Unexpected error from C library during 'pthread_mutex_lock': Resource deadlock avoided.  Aborting.*");
     636                 :           1 :       return;
     637                 :             :     }
     638                 :             : 
     639                 :           1 :   cancellable = g_cancellable_new ();
     640                 :           1 :   thread_data.cancellable = cancellable;
     641                 :           1 :   thread_data.callback = G_CALLBACK (on_cancelled_reset);
     642                 :             : 
     643                 :           1 :   g_assert_false (g_atomic_int_get (&thread_data.is_resetting));
     644                 :           1 :   g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
     645                 :             : 
     646                 :           1 :   thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs",
     647                 :             :                          connecting_thread, &thread_data);
     648                 :             : 
     649                 :       17051 :   while (!g_atomic_pointer_get (&thread_data.loop))
     650                 :             :     ;
     651                 :             : 
     652                 :           1 :   thread_loop = thread_data.loop;
     653                 :           1 :   g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), !=, 0);
     654                 :             : 
     655                 :             :   /* This thread will hang (at least that's what this test wants to ensure), but
     656                 :             :    * we can't stop it from the caller, unless we'll expose pthread_cancel() (and
     657                 :             :    * similar) to GLib. So it will keep hanging until the test subprocess exits.
     658                 :             :    */
     659                 :           1 :   cancelling_thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs",
     660                 :             :                                     (GThreadFunc) g_cancellable_cancel,
     661                 :             :                                     cancellable);
     662                 :             : 
     663                 :       27908 :   while (!g_cancellable_is_cancelled (cancellable) ||
     664                 :         161 :          !g_atomic_int_get (&thread_data.is_resetting))
     665                 :             :     ;
     666                 :             : 
     667                 :           1 :   g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
     668                 :           1 :   g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), >, 0);
     669                 :             : 
     670                 :           1 :   waited = &waited;
     671                 :           1 :   g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited);
     672                 :           2 :   while (waited != NULL)
     673                 :           1 :     g_main_context_iteration (NULL, TRUE);
     674                 :             : 
     675                 :           1 :   g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
     676                 :             : 
     677                 :           1 :   g_main_loop_quit (thread_loop);
     678                 :           1 :   g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
     679                 :             : 
     680                 :           1 :   g_thread_join (g_steal_pointer (&thread));
     681                 :           1 :   g_thread_unref (cancelling_thread);
     682                 :           1 :   g_object_unref (cancellable);
     683                 :             : }
     684                 :             : 
     685                 :             : static gpointer
     686                 :           2 : repeatedly_cancelling_thread (gpointer data)
     687                 :             : {
     688                 :           2 :   GCancellable *cancellable = data;
     689                 :           2 :   const guint iterations = 10000;
     690                 :             : 
     691                 :       20002 :   for (guint i = 0; i < iterations; ++i)
     692                 :       20000 :     g_cancellable_cancel (cancellable);
     693                 :             : 
     694                 :           2 :   return NULL;
     695                 :             : }
     696                 :             : 
     697                 :             : static gpointer
     698                 :           2 : repeatedly_resetting_thread (gpointer data)
     699                 :             : {
     700                 :           2 :   GCancellable *cancellable = data;
     701                 :           2 :   const guint iterations = 10000;
     702                 :             : 
     703                 :       20002 :   for (guint i = 0; i < iterations; ++i)
     704                 :       20000 :     g_cancellable_reset (cancellable);
     705                 :             : 
     706                 :           2 :   return NULL;
     707                 :             : }
     708                 :             : 
     709                 :             : static void
     710                 :           3 : on_racy_cancellable_cancelled (GCancellable *cancellable,
     711                 :             :                                gpointer data)
     712                 :             : {
     713                 :           3 :   gboolean *callback_called = data;
     714                 :             : 
     715                 :           3 :   g_assert_true (g_cancellable_is_cancelled (cancellable));
     716                 :           3 :   g_atomic_int_set (callback_called, TRUE);
     717                 :           3 : }
     718                 :             : 
     719                 :             : static void
     720                 :           1 : test_cancellable_cancel_reset_races (void)
     721                 :             : {
     722                 :             :   GCancellable *cancellable;
     723                 :           1 :   GThread *resetting_thread = NULL;
     724                 :           1 :   GThread *cancelling_thread = NULL;
     725                 :           1 :   gboolean callback_called = FALSE;
     726                 :             : 
     727                 :           1 :   g_test_summary ("Tests threads racing for cancelling and resetting a GCancellable");
     728                 :             : 
     729                 :           1 :   cancellable = g_cancellable_new ();
     730                 :             : 
     731                 :           1 :   g_cancellable_connect (cancellable, G_CALLBACK (on_racy_cancellable_cancelled),
     732                 :             :                          &callback_called, NULL);
     733                 :           1 :   g_assert_false (callback_called);
     734                 :             : 
     735                 :           1 :   resetting_thread = g_thread_new ("/cancellable/cancel-reset-races/resetting",
     736                 :             :                                    repeatedly_resetting_thread,
     737                 :             :                                    cancellable);
     738                 :           1 :   cancelling_thread = g_thread_new ("/cancellable/cancel-reset-races/cancelling",
     739                 :             :                                     repeatedly_cancelling_thread, cancellable);
     740                 :             : 
     741                 :           1 :   g_thread_join (g_steal_pointer (&cancelling_thread));
     742                 :           1 :   g_thread_join (g_steal_pointer (&resetting_thread));
     743                 :             : 
     744                 :           1 :   g_assert_true (callback_called);
     745                 :             : 
     746                 :           1 :   g_object_unref (cancellable);
     747                 :           1 : }
     748                 :             : 
     749                 :             : static gpointer
     750                 :           1 : repeatedly_connecting_thread (gpointer data)
     751                 :             : {
     752                 :           1 :   GCancellable *cancellable = data;
     753                 :           1 :   const guint iterations = 10000;
     754                 :           1 :   gboolean callback_ever_called = FALSE;
     755                 :             : 
     756                 :       10001 :   for (guint i = 0; i < iterations; ++i)
     757                 :             :     {
     758                 :       10000 :       gboolean callback_called = FALSE;
     759                 :             :       gboolean called;
     760                 :       10000 :       gulong id = g_cancellable_connect (cancellable,
     761                 :             :                                          G_CALLBACK (on_racy_cancellable_cancelled),
     762                 :             :                                          &callback_called, NULL);
     763                 :       10000 :       called = g_atomic_int_get (&callback_called);
     764                 :       10000 :       callback_ever_called |= called;
     765                 :       10000 :       if (g_test_verbose () && called)
     766                 :           0 :         g_test_message ("Reconnecting cancellation callback called");
     767                 :       10000 :       g_cancellable_disconnect (cancellable, id);
     768                 :             :     }
     769                 :             : 
     770                 :           1 :   if (!callback_ever_called)
     771                 :           0 :     g_test_incomplete ("We didn't really checked if callbacks is called properly");
     772                 :             : 
     773                 :           1 :   return NULL;
     774                 :             : }
     775                 :             : 
     776                 :             : static void
     777                 :           1 : test_cancellable_cancel_reset_connect_races (void)
     778                 :             : {
     779                 :             :   GCancellable *cancellable;
     780                 :           1 :   GThread *resetting_thread = NULL;
     781                 :           1 :   GThread *cancelling_thread = NULL;
     782                 :           1 :   GThread *connecting_thread = NULL;
     783                 :           1 :   gboolean callback_called = FALSE;
     784                 :             : 
     785                 :           1 :   g_test_summary ("Tests threads racing for cancelling, connecting and disconnecting "
     786                 :             :                   " and resetting a GCancellable");
     787                 :             : 
     788                 :           1 :   cancellable = g_cancellable_new ();
     789                 :             : 
     790                 :           1 :   g_cancellable_connect (cancellable, G_CALLBACK (on_racy_cancellable_cancelled),
     791                 :             :                          &callback_called, NULL);
     792                 :           1 :   g_assert_false (callback_called);
     793                 :             : 
     794                 :           1 :   resetting_thread = g_thread_new ("/cancel-reset-connect-races/resetting",
     795                 :             :                                    repeatedly_resetting_thread,
     796                 :             :                                    cancellable);
     797                 :           1 :   cancelling_thread = g_thread_new ("/cancel-reset-connect-races/cancelling",
     798                 :             :                                     repeatedly_cancelling_thread, cancellable);
     799                 :           1 :   connecting_thread = g_thread_new ("/cancel-reset-connect-races/connecting",
     800                 :             :                                     repeatedly_connecting_thread, cancellable);
     801                 :             : 
     802                 :           1 :   g_thread_join (g_steal_pointer (&cancelling_thread));
     803                 :           1 :   g_thread_join (g_steal_pointer (&resetting_thread));
     804                 :           1 :   g_thread_join (g_steal_pointer (&connecting_thread));
     805                 :             : 
     806                 :           1 :   g_assert_true (callback_called);
     807                 :             : 
     808                 :           1 :   g_object_unref (cancellable);
     809                 :           1 : }
     810                 :             : 
     811                 :             : static gboolean
     812                 :           2 : source_cancelled_counter_cb (GCancellable *cancellable,
     813                 :             :                              gpointer      user_data)
     814                 :             : {
     815                 :           2 :   guint *n_calls = user_data;
     816                 :             : 
     817                 :           2 :   *n_calls = *n_calls + 1;
     818                 :             : 
     819                 :           2 :   return G_SOURCE_CONTINUE;
     820                 :             : }
     821                 :             : 
     822                 :             : static void
     823                 :           0 : do_nothing (G_GNUC_UNUSED void *user_data)
     824                 :             : {
     825                 :             :   /* An empty timeout/idle once callback function */
     826                 :           0 : }
     827                 :             : 
     828                 :             : static void
     829                 :           1 : test_cancellable_source_can_be_fired_multiple_times (void)
     830                 :             : {
     831                 :             :   GCancellable *cancellable;
     832                 :             :   GSource *source;
     833                 :           1 :   guint n_calls = 0;
     834                 :             : 
     835                 :           1 :   g_test_summary ("Test a cancellable source callback can be called multiple times");
     836                 :           1 :   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/774");
     837                 :             : 
     838                 :           1 :   cancellable = g_cancellable_new ();
     839                 :           1 :   source = g_cancellable_source_new (cancellable);
     840                 :             : 
     841                 :           1 :   g_source_set_callback (source, G_SOURCE_FUNC (source_cancelled_counter_cb),
     842                 :             :                          &n_calls, NULL);
     843                 :           1 :   g_source_attach (source, NULL);
     844                 :             : 
     845                 :           1 :   g_cancellable_cancel (cancellable);
     846                 :           1 :   g_assert_cmpuint (n_calls, ==, 0);
     847                 :             : 
     848                 :           2 :   while (g_main_context_pending (NULL))
     849                 :           1 :     g_main_context_iteration (NULL, TRUE);
     850                 :             : 
     851                 :           1 :   g_assert_cmpuint (n_calls, ==, 1);
     852                 :             : 
     853                 :           1 :   g_cancellable_cancel (cancellable);
     854                 :             : 
     855                 :           1 :   g_timeout_add_once (100, do_nothing, NULL);
     856                 :           1 :   while (g_main_context_pending (NULL))
     857                 :           0 :     g_main_context_iteration (NULL, TRUE);
     858                 :             : 
     859                 :           1 :   g_assert_cmpuint (n_calls, ==, 1);
     860                 :             : 
     861                 :           1 :   g_cancellable_reset (cancellable);
     862                 :           1 :   g_cancellable_cancel (cancellable);
     863                 :           1 :   g_assert_cmpuint (n_calls, ==, 1);
     864                 :             : 
     865                 :           2 :   while (g_main_context_pending (NULL))
     866                 :           1 :     g_main_context_iteration (NULL, TRUE);
     867                 :             : 
     868                 :           1 :   g_assert_cmpuint (n_calls, ==, 2);
     869                 :             : 
     870                 :           1 :   g_source_unref (source);
     871                 :           1 :   g_object_unref (cancellable);
     872                 :           1 : }
     873                 :             : 
     874                 :             : int
     875                 :           3 : main (int argc, char *argv[])
     876                 :             : {
     877                 :           3 :   g_test_init (&argc, &argv, NULL);
     878                 :             : 
     879                 :           3 :   g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent);
     880                 :           3 :   g_test_add_func ("/cancellable/null", test_cancel_null);
     881                 :           3 :   g_test_add_func ("/cancellable/disconnect-on-cancelled-callback-hangs", test_cancellable_disconnect_on_cancelled_callback_hangs);
     882                 :           3 :   g_test_add_func ("/cancellable/resets-on-cancel-callback-hangs", test_cancellable_reset_on_cancelled_callback_hangs);
     883                 :           3 :   g_test_add_func ("/cancellable/poll-fd", test_cancellable_poll_fd);
     884                 :           3 :   g_test_add_func ("/cancellable/poll-fd-cancelled", test_cancellable_cancelled_poll_fd);
     885                 :           3 :   g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded);
     886                 :           3 :   g_test_add_func ("/cancellable/cancel-reset-races", test_cancellable_cancel_reset_races);
     887                 :           3 :   g_test_add_func ("/cancellable/cancel-reset-connect-races", test_cancellable_cancel_reset_connect_races);
     888                 :           3 :   g_test_add_func ("/cancellable-source/threaded-dispose", test_cancellable_source_threaded_dispose);
     889                 :           3 :   g_test_add_func ("/cancellable-source/can-be-fired-multiple-times", test_cancellable_source_can_be_fired_multiple_times);
     890                 :             : 
     891                 :           3 :   return g_test_run ();
     892                 :             : }
        

Generated by: LCOV version 2.0-1