(root)/
glib-2.79.0/
gio/
tests/
cancellable.c
       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  mock_operation_free (gpointer user_data)
      42  {
      43    MockOperationData *data = user_data;
      44    g_free (data);
      45  }
      46  
      47  static void
      48  mock_operation_thread (GTask        *task,
      49                         gpointer      source_object,
      50                         gpointer      task_data,
      51                         GCancellable *cancellable)
      52  {
      53    MockOperationData *data = task_data;
      54    guint i;
      55  
      56    for (i = 0; i < data->iterations_requested; i++)
      57      {
      58        if (g_cancellable_is_cancelled (cancellable))
      59          break;
      60        if (g_test_verbose ())
      61          g_test_message ("THRD: %u iteration %u", data->iterations_requested, i);
      62        g_usleep (WAIT_ITERATION * 1000);
      63      }
      64  
      65    if (g_test_verbose ())
      66      g_test_message ("THRD: %u stopped at %u", data->iterations_requested, i);
      67    g_atomic_int_add (&data->iterations_done, i);
      68  
      69    g_task_return_boolean (task, TRUE);
      70  }
      71  
      72  static gboolean
      73  mock_operation_timeout (gpointer user_data)
      74  {
      75    GTask *task;
      76    MockOperationData *data;
      77    gboolean done = FALSE;
      78    guint iterations_done;
      79  
      80    task = G_TASK (user_data);
      81    data = g_task_get_task_data (task);
      82    iterations_done = g_atomic_int_get (&data->iterations_done);
      83  
      84    if (iterations_done >= data->iterations_requested)
      85        done = TRUE;
      86  
      87    if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
      88        done = TRUE;
      89  
      90    if (done)
      91      {
      92        if (g_test_verbose ())
      93          g_test_message ("LOOP: %u stopped at %u",
      94                          data->iterations_requested, iterations_done);
      95        g_task_return_boolean (task, TRUE);
      96        return G_SOURCE_REMOVE;
      97      }
      98    else
      99      {
     100        g_atomic_int_inc (&data->iterations_done);
     101        if (g_test_verbose ())
     102          g_test_message ("LOOP: %u iteration %u",
     103                          data->iterations_requested, iterations_done + 1);
     104        return G_SOURCE_CONTINUE;
     105      }
     106  }
     107  
     108  static void
     109  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    task = g_task_new (NULL, cancellable, callback, user_data);
     119    data = g_new0 (MockOperationData, 1);
     120    data->iterations_requested = wait_iterations;
     121    g_task_set_task_data (task, data, mock_operation_free);
     122  
     123    if (run_in_thread)
     124      {
     125        g_task_run_in_thread (task, mock_operation_thread);
     126        if (g_test_verbose ())
     127          g_test_message ("THRD: %d started", wait_iterations);
     128      }
     129    else
     130      {
     131        g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
     132                            g_object_ref (task), g_object_unref);
     133        if (g_test_verbose ())
     134          g_test_message ("LOOP: %d started", wait_iterations);
     135      }
     136  
     137    g_object_unref (task);
     138  }
     139  
     140  static guint
     141  mock_operation_finish (GAsyncResult  *result,
     142                         GError       **error)
     143  {
     144    MockOperationData *data;
     145    GTask *task;
     146  
     147    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    task = G_TASK (result);
     153    data = g_task_get_task_data (task);
     154  
     155    g_task_propagate_boolean (task, error);
     156    return g_atomic_int_get (&data->iterations_done);
     157  }
     158  
     159  static void
     160  on_mock_operation_ready (GObject      *source,
     161                           GAsyncResult *result,
     162                           gpointer      user_data)
     163  {
     164    guint iterations_requested;
     165    guint iterations_done;
     166    GError *error = NULL;
     167  
     168    iterations_requested = GPOINTER_TO_UINT (user_data);
     169    iterations_done = mock_operation_finish (result, &error);
     170  
     171    g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
     172    g_error_free (error);
     173  
     174    g_assert_cmpint (iterations_requested, >, iterations_done);
     175    num_async_operations--;
     176    g_main_context_wakeup (NULL);
     177  }
     178  
     179  static void
     180  test_cancel_multiple_concurrent (void)
     181  {
     182    GCancellable *cancellable;
     183    guint i, iterations;
     184  
     185    if (!g_test_thorough ())
     186      {
     187        g_test_skip ("Not running timing heavy test");
     188        return;
     189      }
     190  
     191    cancellable = g_cancellable_new ();
     192  
     193    for (i = 0; i < 45; i++)
     194      {
     195        iterations = i + 10;
     196        mock_operation_async (iterations, g_random_boolean (), cancellable,
     197                              on_mock_operation_ready, GUINT_TO_POINTER (iterations));
     198        num_async_operations++;
     199      }
     200  
     201    /* Wait for the threads to start up */
     202    while (num_async_operations != 45)
     203      g_main_context_iteration (NULL, TRUE);
     204    g_assert_cmpint (num_async_operations, ==, 45);\
     205  
     206    if (g_test_verbose ())
     207      g_test_message ("CANCEL: %d operations", num_async_operations);
     208    g_cancellable_cancel (cancellable);
     209    g_assert_true (g_cancellable_is_cancelled (cancellable));
     210  
     211    /* Wait for all operations to be cancelled */
     212    while (num_async_operations != 0)
     213      g_main_context_iteration (NULL, TRUE);
     214    g_assert_cmpint (num_async_operations, ==, 0);
     215  
     216    g_object_unref (cancellable);
     217  }
     218  
     219  static void
     220  test_cancel_null (void)
     221  {
     222    g_cancellable_cancel (NULL);
     223  }
     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  cancelled_cb (GCancellable *cancellable,
     235                gpointer      user_data)
     236  {
     237    /* Nothing needs to be done here. */
     238    return G_SOURCE_CONTINUE;
     239  }
     240  
     241  static gpointer
     242  threaded_dispose_thread_cb (gpointer user_data)
     243  {
     244    ThreadedDisposeData *data = user_data;
     245    GSource *cancellable_source;
     246  
     247    g_mutex_lock (&data->mutex);
     248    data->thread_ready = TRUE;
     249    g_cond_broadcast (&data->cond);
     250    g_mutex_unlock (&data->mutex);
     251  
     252    while ((cancellable_source = g_async_queue_pop (data->cancellable_source_queue)) != (gpointer) 1)
     253      {
     254        /* Race with cancellation of the cancellable. */
     255        g_source_unref (cancellable_source);
     256      }
     257  
     258    return NULL;
     259  }
     260  
     261  static void
     262  test_cancellable_source_threaded_dispose (void)
     263  {
     264  #ifdef _GLIB_ADDRESS_SANITIZER
     265    g_test_incomplete ("FIXME: Leaks lots of GCancellableSource objects, see glib#2309");
     266    (void) cancelled_cb;
     267    (void) threaded_dispose_thread_cb;
     268  #else
     269    ThreadedDisposeData data;
     270    GThread *thread = NULL;
     271    guint i;
     272    GPtrArray *cancellables_pending_unref = g_ptr_array_new_with_free_func (g_object_unref);
     273  
     274    g_test_summary ("Test a thread race between disposing of a GCancellableSource "
     275                    "(in one thread) and cancelling the GCancellable it refers "
     276                    "to (in another thread)");
     277    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1841");
     278  
     279    /* Create a new thread and wait until it’s ready to execute. Each iteration of
     280     * the test will pass it a new #GCancellableSource. */
     281    g_cond_init (&data.cond);
     282    g_mutex_init (&data.mutex);
     283    data.cancellable_source_queue = g_async_queue_new_full ((GDestroyNotify) g_source_unref);
     284    data.thread_ready = FALSE;
     285  
     286    g_mutex_lock (&data.mutex);
     287    thread = g_thread_new ("/cancellable-source/threaded-dispose",
     288                           threaded_dispose_thread_cb, &data);
     289  
     290    while (!data.thread_ready)
     291      g_cond_wait (&data.cond, &data.mutex);
     292    g_mutex_unlock (&data.mutex);
     293  
     294    for (i = 0; i < 100000; i++)
     295      {
     296        GCancellable *cancellable = NULL;
     297        GSource *cancellable_source = NULL;
     298  
     299        /* Create a cancellable and a cancellable source for it. For this test,
     300         * there’s no need to attach the source to a #GMainContext. */
     301        cancellable = g_cancellable_new ();
     302        cancellable_source = g_cancellable_source_new (cancellable);
     303        g_source_set_callback (cancellable_source, G_SOURCE_FUNC (cancelled_cb), NULL, NULL);
     304  
     305        /* Send it to the thread and wait until it’s ready to execute before
     306         * cancelling our cancellable. */
     307        g_async_queue_push (data.cancellable_source_queue, g_steal_pointer (&cancellable_source));
     308  
     309        /* Race with disposal of the cancellable source. */
     310        g_cancellable_cancel (cancellable);
     311  
     312        /* This thread can’t drop its reference to the #GCancellable here, as it
     313         * might not be the final reference (depending on how the race is
     314         * resolved: #GCancellableSource holds a strong ref on the #GCancellable),
     315         * and at this point we can’t guarantee to support disposing of a
     316         * #GCancellable in a different thread from where it’s created, especially
     317         * when signal handlers are connected to it.
     318         *
     319         * So this is a workaround for a disposal-in-another-thread bug for
     320         * #GCancellable, but there’s no hope of debugging and resolving it with
     321         * this test setup, and the bug is orthogonal to what’s being tested here
     322         * (a race between #GCancellable and #GCancellableSource). */
     323        g_ptr_array_add (cancellables_pending_unref, g_steal_pointer (&cancellable));
     324      }
     325  
     326    /* Indicate that the test has finished. Can’t use %NULL as #GAsyncQueue
     327     * doesn’t allow that.*/
     328    g_async_queue_push (data.cancellable_source_queue, (gpointer) 1);
     329  
     330    g_thread_join (g_steal_pointer (&thread));
     331  
     332    g_assert (g_async_queue_length (data.cancellable_source_queue) == 0);
     333    g_async_queue_unref (data.cancellable_source_queue);
     334    g_mutex_clear (&data.mutex);
     335    g_cond_clear (&data.cond);
     336  
     337    g_ptr_array_unref (cancellables_pending_unref);
     338  #endif
     339  }
     340  
     341  static void
     342  test_cancellable_poll_fd (void)
     343  {
     344    GCancellable *cancellable;
     345    GPollFD pollfd = {.fd = -1};
     346    int fd = -1;
     347  
     348  #ifdef G_OS_WIN32
     349    g_test_skip ("Platform not supported");
     350    return;
     351  #endif
     352  
     353    cancellable = g_cancellable_new ();
     354  
     355    g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd));
     356    g_assert_cmpint (pollfd.fd, >, 0);
     357  
     358    fd = g_cancellable_get_fd (cancellable);
     359    g_assert_cmpint (fd, >, 0);
     360  
     361    g_cancellable_release_fd (cancellable);
     362    g_cancellable_release_fd (cancellable);
     363  
     364    g_object_unref (cancellable);
     365  }
     366  
     367  static void
     368  test_cancellable_cancelled_poll_fd (void)
     369  {
     370    GCancellable *cancellable;
     371    GPollFD pollfd;
     372  
     373  #ifdef G_OS_WIN32
     374    g_test_skip ("Platform not supported");
     375    return;
     376  #endif
     377  
     378    g_test_summary ("Tests that cancellation wakes up a pollable FD on creation");
     379  
     380    cancellable = g_cancellable_new ();
     381    g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd));
     382    g_cancellable_cancel (cancellable);
     383  
     384    g_poll (&pollfd, 1, -1);
     385  
     386    g_cancellable_release_fd (cancellable);
     387    g_object_unref (cancellable);
     388  }
     389  
     390  typedef struct {
     391    GCancellable *cancellable;
     392    gboolean polling_started; /* Atomic */
     393  } CancellablePollThreadData;
     394  
     395  static gpointer
     396  cancel_cancellable_thread (gpointer user_data)
     397  {
     398    CancellablePollThreadData *thread_data = user_data;
     399  
     400    while (!g_atomic_int_get (&thread_data->polling_started))
     401      ;
     402  
     403    /* Let's just wait a moment before cancelling, this is not really needed
     404     * but we do it to simulate that the thread is actually doing something.
     405     */
     406    g_usleep (G_USEC_PER_SEC / 10);
     407    g_cancellable_cancel (thread_data->cancellable);
     408  
     409    return NULL;
     410  }
     411  
     412  static gpointer
     413  polling_cancelled_cancellable_thread (gpointer user_data)
     414  {
     415    CancellablePollThreadData *thread_data = user_data;
     416    GPollFD pollfd;
     417  
     418    g_assert_true (g_cancellable_make_pollfd (thread_data->cancellable, &pollfd));
     419    g_atomic_int_set (&thread_data->polling_started, TRUE);
     420  
     421    g_poll (&pollfd, 1, -1);
     422  
     423    g_cancellable_release_fd (thread_data->cancellable);
     424  
     425    return NULL;
     426  }
     427  
     428  static void
     429  test_cancellable_cancelled_poll_fd_threaded (void)
     430  {
     431    GCancellable *cancellable;
     432    CancellablePollThreadData thread_data = {0};
     433    GThread *polling_thread = NULL;
     434    GThread *cancelling_thread = NULL;
     435    GPollFD pollfd;
     436  
     437  #ifdef G_OS_WIN32
     438    g_test_skip ("Platform not supported");
     439    return;
     440  #endif
     441  
     442    g_test_summary ("Tests that a cancellation wakes up a pollable FD");
     443  
     444    cancellable = g_cancellable_new ();
     445    g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd));
     446  
     447    thread_data.cancellable = cancellable;
     448  
     449    polling_thread = g_thread_new ("/cancellable/poll-fd-cancelled-threaded/polling",
     450                                   polling_cancelled_cancellable_thread,
     451                                   &thread_data);
     452    cancelling_thread = g_thread_new ("/cancellable/poll-fd-cancelled-threaded/cancelling",
     453                                      cancel_cancellable_thread, &thread_data);
     454  
     455    g_poll (&pollfd, 1, -1);
     456    g_assert_true (g_cancellable_is_cancelled (cancellable));
     457    g_cancellable_release_fd (cancellable);
     458  
     459    g_thread_join (g_steal_pointer (&cancelling_thread));
     460    g_thread_join (g_steal_pointer (&polling_thread));
     461  
     462    g_object_unref (cancellable);
     463  }
     464  
     465  typedef struct {
     466    GMainLoop *loop;
     467    GCancellable *cancellable;
     468    GCallback callback;
     469    gboolean is_disconnecting;
     470    gboolean is_resetting;
     471    gpointer handler_id;
     472  } ConnectingThreadData;
     473  
     474  static void
     475  on_cancellable_connect_disconnect (GCancellable *cancellable,
     476                                     ConnectingThreadData *data)
     477  {
     478    gulong handler_id = (gulong) (guintptr) g_atomic_pointer_exchange (&data->handler_id, 0);
     479    g_atomic_int_set (&data->is_disconnecting, TRUE);
     480    g_cancellable_disconnect (cancellable, handler_id);
     481    g_atomic_int_set (&data->is_disconnecting, FALSE);
     482  }
     483  
     484  static gpointer
     485  connecting_thread (gpointer user_data)
     486  {
     487    GMainContext *context;
     488    ConnectingThreadData *data = user_data;
     489    gulong handler_id;
     490    GMainLoop *loop;
     491  
     492    handler_id =
     493      g_cancellable_connect (data->cancellable, data->callback, data, NULL);
     494  
     495    context = g_main_context_new ();
     496    g_main_context_push_thread_default (context);
     497    loop = g_main_loop_new (context, FALSE);
     498  
     499    g_atomic_pointer_set (&data->handler_id, (gpointer) (guintptr) handler_id);
     500    g_atomic_pointer_set (&data->loop, loop);
     501    g_main_loop_run (loop);
     502  
     503    g_main_context_pop_thread_default (context);
     504    g_main_context_unref (context);
     505    g_main_loop_unref (loop);
     506  
     507    return NULL;
     508  }
     509  
     510  static void
     511  test_cancellable_disconnect_on_cancelled_callback_hangs (void)
     512  {
     513    GCancellable *cancellable;
     514    GThread *thread = NULL;
     515    GThread *cancelling_thread = NULL;
     516    ConnectingThreadData thread_data = {0};
     517    GMainLoop *thread_loop;
     518    gpointer waited;
     519  
     520    /* While this is not convenient, it's done to ensure that we don't have a
     521     * race when trying to cancelling a cancellable that is about to be cancelled
     522     * in another thread
     523     */
     524    g_test_summary ("Tests that trying to disconnect a cancellable from the "
     525                    "cancelled signal callback will result in a deadlock "
     526                    "as per #GCancellable::cancelled");
     527  
     528    if (!g_test_undefined ())
     529      {
     530        g_test_skip ("Skipping testing disallowed behaviour of disconnecting from "
     531                    "a cancellable from its cancelled callback");
     532        return;
     533      }
     534  
     535    cancellable = g_cancellable_new ();
     536    thread_data.cancellable = cancellable;
     537    thread_data.callback = G_CALLBACK (on_cancellable_connect_disconnect);
     538  
     539    g_assert_false (g_atomic_int_get (&thread_data.is_disconnecting));
     540    g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
     541  
     542    thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs",
     543                           connecting_thread, &thread_data);
     544  
     545    while (!g_atomic_pointer_get (&thread_data.loop))
     546      ;
     547  
     548    thread_loop = thread_data.loop;
     549    g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), !=, 0);
     550  
     551    /* FIXME: This thread will hang (at least that's what this test wants to
     552     *        ensure), but we can't stop it from the caller, unless we'll expose
     553     *        pthread_cancel (and similar) to GLib.
     554     *        So it will keep hanging till the test process is alive.
     555     */
     556    cancelling_thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs",
     557                                      (GThreadFunc) g_cancellable_cancel,
     558                                      cancellable);
     559  
     560    while (!g_cancellable_is_cancelled (cancellable) ||
     561           !g_atomic_int_get (&thread_data.is_disconnecting))
     562      ;
     563  
     564    g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting));
     565    g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
     566  
     567    waited = &waited;
     568    g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited);
     569    while (waited != NULL)
     570      g_main_context_iteration (NULL, TRUE);
     571  
     572    g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting));
     573  
     574    g_main_loop_quit (thread_loop);
     575    g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting));
     576  
     577    g_thread_join (g_steal_pointer (&thread));
     578    g_thread_unref (cancelling_thread);
     579    g_object_unref (cancellable);
     580  }
     581  
     582  static void
     583  on_cancelled_reset (GCancellable *cancellable,
     584                      gpointer data)
     585  {
     586    ConnectingThreadData *thread_data = data;
     587  
     588    g_assert_true (g_cancellable_is_cancelled (cancellable));
     589    g_atomic_int_set (&thread_data->is_resetting, TRUE);
     590    g_cancellable_reset (cancellable);
     591    g_assert_false (g_cancellable_is_cancelled (cancellable));
     592    g_atomic_int_set (&thread_data->is_resetting, TRUE);
     593  }
     594  
     595  static void
     596  test_cancellable_reset_on_cancelled_callback_hangs (void)
     597  {
     598    GCancellable *cancellable;
     599    GThread *thread = NULL;
     600    GThread *cancelling_thread = NULL;
     601    ConnectingThreadData thread_data = {0};
     602    GMainLoop *thread_loop;
     603    gpointer waited;
     604  
     605    /* While this is not convenient, it's done to ensure that we don't have a
     606     * race when trying to cancelling a cancellable that is about to be cancelled
     607     * in another thread
     608     */
     609    g_test_summary ("Tests that trying to reset a cancellable from the "
     610                    "cancelled signal callback will result in a deadlock "
     611                    "as per #GCancellable::cancelled");
     612  
     613    if (!g_test_undefined ())
     614      {
     615        g_test_skip ("Skipping testing disallowed behaviour of resetting a "
     616                    "cancellable from its callback");
     617        return;
     618      }
     619  
     620    cancellable = g_cancellable_new ();
     621    thread_data.cancellable = cancellable;
     622    thread_data.callback = G_CALLBACK (on_cancelled_reset);
     623  
     624    g_assert_false (g_atomic_int_get (&thread_data.is_resetting));
     625    g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
     626  
     627    thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs",
     628                           connecting_thread, &thread_data);
     629  
     630    while (!g_atomic_pointer_get (&thread_data.loop))
     631      ;
     632  
     633    thread_loop = thread_data.loop;
     634    g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), !=, 0);
     635  
     636    /* FIXME: This thread will hang (at least that's what this test wants to
     637     *        ensure), but we can't stop it from the caller, unless we'll expose
     638     *        pthread_cancel (and similar) to GLib.
     639     *        So it will keep hanging till the test process is alive.
     640     */
     641    cancelling_thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs",
     642                                      (GThreadFunc) g_cancellable_cancel,
     643                                      cancellable);
     644  
     645    while (!g_cancellable_is_cancelled (cancellable) ||
     646           !g_atomic_int_get (&thread_data.is_resetting))
     647      ;
     648  
     649    g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
     650    g_assert_cmpuint ((gulong) (guintptr) g_atomic_pointer_get (&thread_data.handler_id), >, 0);
     651  
     652    waited = &waited;
     653    g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited);
     654    while (waited != NULL)
     655      g_main_context_iteration (NULL, TRUE);
     656  
     657    g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
     658  
     659    g_main_loop_quit (thread_loop);
     660    g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
     661  
     662    g_thread_join (g_steal_pointer (&thread));
     663    g_thread_unref (cancelling_thread);
     664    g_object_unref (cancellable);
     665  }
     666  
     667  static gpointer
     668  repeatedly_cancelling_thread (gpointer data)
     669  {
     670    GCancellable *cancellable = data;
     671    const guint iterations = 10000;
     672  
     673    for (guint i = 0; i < iterations; ++i)
     674      g_cancellable_cancel (cancellable);
     675  
     676    return NULL;
     677  }
     678  
     679  static gpointer
     680  repeatedly_resetting_thread (gpointer data)
     681  {
     682    GCancellable *cancellable = data;
     683    const guint iterations = 10000;
     684  
     685    for (guint i = 0; i < iterations; ++i)
     686      g_cancellable_reset (cancellable);
     687  
     688    return NULL;
     689  }
     690  
     691  static void
     692  on_racy_cancellable_cancelled (GCancellable *cancellable,
     693                                 gpointer data)
     694  {
     695    gboolean *callback_called = data;
     696  
     697    g_assert_true (g_cancellable_is_cancelled (cancellable));
     698    g_atomic_int_set (callback_called, TRUE);
     699  }
     700  
     701  static void
     702  test_cancellable_cancel_reset_races (void)
     703  {
     704    GCancellable *cancellable;
     705    GThread *resetting_thread = NULL;
     706    GThread *cancelling_thread = NULL;
     707    gboolean callback_called = FALSE;
     708  
     709    g_test_summary ("Tests threads racing for cancelling and resetting a GCancellable");
     710  
     711    cancellable = g_cancellable_new ();
     712  
     713    g_cancellable_connect (cancellable, G_CALLBACK (on_racy_cancellable_cancelled),
     714                           &callback_called, NULL);
     715    g_assert_false (callback_called);
     716  
     717    resetting_thread = g_thread_new ("/cancellable/cancel-reset-races/resetting",
     718                                     repeatedly_resetting_thread,
     719                                     cancellable);
     720    cancelling_thread = g_thread_new ("/cancellable/cancel-reset-races/cancelling",
     721                                      repeatedly_cancelling_thread, cancellable);
     722  
     723    g_thread_join (g_steal_pointer (&cancelling_thread));
     724    g_thread_join (g_steal_pointer (&resetting_thread));
     725  
     726    g_assert_true (callback_called);
     727  
     728    g_object_unref (cancellable);
     729  }
     730  
     731  static gpointer
     732  repeatedly_connecting_thread (gpointer data)
     733  {
     734    GCancellable *cancellable = data;
     735    const guint iterations = 10000;
     736    gboolean callback_ever_called = FALSE;
     737  
     738    for (guint i = 0; i < iterations; ++i)
     739      {
     740        gboolean callback_called = FALSE;
     741        gboolean called;
     742        gulong id = g_cancellable_connect (cancellable,
     743                                           G_CALLBACK (on_racy_cancellable_cancelled),
     744                                           &callback_called, NULL);
     745        called = g_atomic_int_get (&callback_called);
     746        callback_ever_called |= called;
     747        if (g_test_verbose () && called)
     748          g_test_message ("Reconnecting cancellation callback called");
     749        g_cancellable_disconnect (cancellable, id);
     750      }
     751  
     752    if (!callback_ever_called)
     753      g_test_incomplete ("We didn't really checked if callbacks is called properly");
     754  
     755    return NULL;
     756  }
     757  
     758  static void
     759  test_cancellable_cancel_reset_connect_races (void)
     760  {
     761    GCancellable *cancellable;
     762    GThread *resetting_thread = NULL;
     763    GThread *cancelling_thread = NULL;
     764    GThread *connecting_thread = NULL;
     765    gboolean callback_called = FALSE;
     766  
     767    g_test_summary ("Tests threads racing for cancelling, connecting and disconnecting "
     768                    " and resetting a GCancellable");
     769  
     770    cancellable = g_cancellable_new ();
     771  
     772    g_cancellable_connect (cancellable, G_CALLBACK (on_racy_cancellable_cancelled),
     773                           &callback_called, NULL);
     774    g_assert_false (callback_called);
     775  
     776    resetting_thread = g_thread_new ("/cancel-reset-connect-races/resetting",
     777                                     repeatedly_resetting_thread,
     778                                     cancellable);
     779    cancelling_thread = g_thread_new ("/cancel-reset-connect-races/cancelling",
     780                                      repeatedly_cancelling_thread, cancellable);
     781    connecting_thread = g_thread_new ("/cancel-reset-connect-races/connecting",
     782                                      repeatedly_connecting_thread, cancellable);
     783  
     784    g_thread_join (g_steal_pointer (&cancelling_thread));
     785    g_thread_join (g_steal_pointer (&resetting_thread));
     786    g_thread_join (g_steal_pointer (&connecting_thread));
     787  
     788    g_assert_true (callback_called);
     789  
     790    g_object_unref (cancellable);
     791  }
     792  
     793  int
     794  main (int argc, char *argv[])
     795  {
     796    g_test_init (&argc, &argv, NULL);
     797  
     798    g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent);
     799    g_test_add_func ("/cancellable/null", test_cancel_null);
     800    g_test_add_func ("/cancellable/disconnect-on-cancelled-callback-hangs", test_cancellable_disconnect_on_cancelled_callback_hangs);
     801    g_test_add_func ("/cancellable/resets-on-cancel-callback-hangs", test_cancellable_reset_on_cancelled_callback_hangs);
     802    g_test_add_func ("/cancellable/poll-fd", test_cancellable_poll_fd);
     803    g_test_add_func ("/cancellable/poll-fd-cancelled", test_cancellable_cancelled_poll_fd);
     804    g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded);
     805    g_test_add_func ("/cancellable/cancel-reset-races", test_cancellable_cancel_reset_races);
     806    g_test_add_func ("/cancellable/cancel-reset-connect-races", test_cancellable_cancel_reset_connect_races);
     807    g_test_add_func ("/cancellable-source/threaded-dispose", test_cancellable_source_threaded_dispose);
     808  
     809    return g_test_run ();
     810  }