[glib: 3/10] gio/tests: Ensure that a cancellable hangs if reset from cancellable callback




commit e7269a26e447c3fab85e823079d000d298cb0931
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Wed Jun 22 03:44:20 2022 +0200

    gio/tests: Ensure that a cancellable hangs if reset from cancellable callback

 gio/tests/cancellable.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
---
diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c
index d27cd7c748..5d8bf8a852 100644
--- a/gio/tests/cancellable.c
+++ b/gio/tests/cancellable.c
@@ -467,6 +467,7 @@ typedef struct {
   GCancellable *cancellable;
   GCallback callback;
   gboolean is_disconnecting;
+  gboolean is_resetting;
   gulong handler_id;
 } ConnectingThreadData;
 
@@ -578,6 +579,91 @@ test_cancellable_disconnect_on_cancelled_callback_hangs (void)
   g_object_unref (cancellable);
 }
 
+static void
+on_cancelled_reset (GCancellable *cancellable,
+                    gpointer data)
+{
+  ConnectingThreadData *thread_data = data;
+
+  g_assert_true (g_cancellable_is_cancelled (cancellable));
+  g_atomic_int_set (&thread_data->is_resetting, TRUE);
+  g_cancellable_reset (cancellable);
+  g_assert_false (g_cancellable_is_cancelled (cancellable));
+  g_atomic_int_set (&thread_data->is_resetting, TRUE);
+}
+
+static void
+test_cancellable_reset_on_cancelled_callback_hangs (void)
+{
+  GCancellable *cancellable;
+  GThread *thread = NULL;
+  GThread *cancelling_thread = NULL;
+  ConnectingThreadData thread_data = {0};
+  GMainLoop *thread_loop;
+  gpointer waited;
+
+  /* While this is not convenient, it's done to ensure that we don't have a
+   * race when trying to cancelling a cancellable that is about to be cancelled
+   * in another thread
+   */
+  g_test_summary ("Tests that trying to reset a cancellable from the "
+                  "cancelled signal callback will result in a deadlock "
+                  "as per #GCancellable::cancelled");
+
+  if (!g_test_undefined ())
+    {
+      g_test_skip ("Skipping testing disallowed behaviour of resetting a "
+                  "cancellable from its callback");
+      return;
+    }
+
+  cancellable = g_cancellable_new ();
+  thread_data.cancellable = cancellable;
+  thread_data.callback = G_CALLBACK (on_cancelled_reset);
+
+  g_assert_false (g_atomic_int_get (&thread_data.is_resetting));
+  g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), ==, 0);
+
+  thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs",
+                         connecting_thread, &thread_data);
+
+  while (!g_atomic_pointer_get (&thread_data.loop))
+    ;
+
+  thread_loop = thread_data.loop;
+  g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), !=, 0);
+
+  /* FIXME: This thread will hang (at least that's what this test wants to
+   *        ensure), but we can't stop it from the caller, unless we'll expose
+   *        pthread_cancel (and similar) to GLib.
+   *        So it will keep hanging till the test process is alive.
+   */
+  cancelling_thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs",
+                                    (GThreadFunc) g_cancellable_cancel,
+                                    cancellable);
+
+  while (!g_cancellable_is_cancelled (cancellable) ||
+         !g_atomic_int_get (&thread_data.is_resetting))
+    ;
+
+  g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
+  g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), >, 0);
+
+  waited = &waited;
+  g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited);
+  while (waited != NULL)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
+
+  g_main_loop_quit (thread_loop);
+  g_assert_true (g_atomic_int_get (&thread_data.is_resetting));
+
+  g_thread_join (g_steal_pointer (&thread));
+  g_thread_unref (cancelling_thread);
+  g_object_unref (cancellable);
+}
+
 static gpointer
 repeatedly_cancelling_thread (gpointer data)
 {
@@ -650,6 +736,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent);
   g_test_add_func ("/cancellable/null", test_cancel_null);
   g_test_add_func ("/cancellable/disconnect-on-cancelled-callback-hangs", 
test_cancellable_disconnect_on_cancelled_callback_hangs);
+  g_test_add_func ("/cancellable/resets-on-cancel-callback-hangs", 
test_cancellable_reset_on_cancelled_callback_hangs);
   g_test_add_func ("/cancellable/poll-fd", test_cancellable_poll_fd);
   g_test_add_func ("/cancellable/poll-fd-cancelled", test_cancellable_cancelled_poll_fd);
   g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]