diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c index e53ae6e7e..d6b52c1ea 100644 --- a/gio/tests/cancellable.c +++ b/gio/tests/cancellable.c @@ -29,8 +29,8 @@ static gint num_async_operations = 0; typedef struct { - guint iterations_requested; - guint iterations_done; + guint iterations_requested; /* construct-only */ + guint iterations_done; /* (atomic) */ } MockOperationData; static void @@ -54,13 +54,13 @@ mock_operation_thread (GTask *task, if (g_cancellable_is_cancelled (cancellable)) break; if (g_test_verbose ()) - g_printerr ("THRD: %u iteration %u\n", data->iterations_requested, i); + g_test_message ("THRD: %u iteration %u", data->iterations_requested, i); g_usleep (WAIT_ITERATION * 1000); } if (g_test_verbose ()) - g_printerr ("THRD: %u stopped at %u\n", data->iterations_requested, i); - data->iterations_done = i; + g_test_message ("THRD: %u stopped at %u", data->iterations_requested, i); + g_atomic_int_add (&data->iterations_done, i); g_task_return_boolean (task, TRUE); } @@ -71,11 +71,13 @@ mock_operation_timeout (gpointer user_data) GTask *task; MockOperationData *data; gboolean done = FALSE; + guint iterations_done; task = G_TASK (user_data); data = g_task_get_task_data (task); + iterations_done = g_atomic_int_get (&data->iterations_done); - if (data->iterations_done >= data->iterations_requested) + if (iterations_done >= data->iterations_requested) done = TRUE; if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) @@ -84,18 +86,18 @@ mock_operation_timeout (gpointer user_data) if (done) { if (g_test_verbose ()) - g_printerr ("LOOP: %u stopped at %u\n", data->iterations_requested,\ - data->iterations_done); + g_test_message ("LOOP: %u stopped at %u", + data->iterations_requested, iterations_done); g_task_return_boolean (task, TRUE); - return FALSE; /* don't call timeout again */ + return G_SOURCE_REMOVE; } else { - data->iterations_done++; + g_atomic_int_inc (&data->iterations_done); if (g_test_verbose ()) - g_printerr ("LOOP: %u iteration %u\n", data->iterations_requested, - data->iterations_done); - return TRUE; /* call timeout */ + g_test_message ("LOOP: %u iteration %u", + data->iterations_requested, iterations_done + 1); + return G_SOURCE_CONTINUE; } } @@ -118,14 +120,14 @@ mock_operation_async (guint wait_iterations, { g_task_run_in_thread (task, mock_operation_thread); if (g_test_verbose ()) - g_printerr ("THRD: %d started\n", wait_iterations); + g_test_message ("THRD: %d started", wait_iterations); } else { g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout, g_object_ref (task), g_object_unref); if (g_test_verbose ()) - g_printerr ("LOOP: %d started\n", wait_iterations); + g_test_message ("LOOP: %d started", wait_iterations); } g_object_unref (task); @@ -147,11 +149,9 @@ mock_operation_finish (GAsyncResult *result, data = g_task_get_task_data (task); g_task_propagate_boolean (task, error); - return data->iterations_done; + return g_atomic_int_get (&data->iterations_done); } -GMainLoop *loop; - static void on_mock_operation_ready (GObject *source, GAsyncResult *result, @@ -169,17 +169,7 @@ on_mock_operation_ready (GObject *source, g_assert_cmpint (iterations_requested, >, iterations_done); num_async_operations--; - - if (!num_async_operations) - g_main_loop_quit (loop); -} - -static gboolean -on_main_loop_timeout_quit (gpointer user_data) -{ - GMainLoop *loop = user_data; - g_main_loop_quit (loop); - return FALSE; + g_main_context_wakeup (NULL); } static void @@ -195,7 +185,6 @@ test_cancel_multiple_concurrent (void) } cancellable = g_cancellable_new (); - loop = g_main_loop_new (NULL, FALSE); for (i = 0; i < 45; i++) { @@ -205,21 +194,22 @@ test_cancel_multiple_concurrent (void) num_async_operations++; } - /* Wait for two iterations, to give threads a chance to start up */ - g_timeout_add (WAIT_ITERATION * 2, on_main_loop_timeout_quit, loop); - g_main_loop_run (loop); - g_assert_cmpint (num_async_operations, ==, 45); + /* Wait for the threads to start up */ + while (num_async_operations != 45) + g_main_context_iteration (NULL, TRUE); + g_assert_cmpint (num_async_operations, ==, 45);\ + if (g_test_verbose ()) - g_printerr ("CANCEL: %d operations\n", num_async_operations); + g_test_message ("CANCEL: %d operations", num_async_operations); g_cancellable_cancel (cancellable); g_assert_true (g_cancellable_is_cancelled (cancellable)); /* Wait for all operations to be cancelled */ - g_main_loop_run (loop); + while (num_async_operations != 0) + g_main_context_iteration (NULL, TRUE); g_assert_cmpint (num_async_operations, ==, 0); g_object_unref (cancellable); - g_main_loop_unref (loop); } static void @@ -270,6 +260,7 @@ test_cancellable_source_threaded_dispose (void) ThreadedDisposeData data; GThread *thread = NULL; guint i; + GPtrArray *cancellables_pending_unref = g_ptr_array_new_with_free_func (g_object_unref); g_test_summary ("Test a thread race between disposing of a GCancellableSource " "(in one thread) and cancelling the GCancellable it refers " @@ -309,7 +300,18 @@ test_cancellable_source_threaded_dispose (void) /* Race with disposal of the cancellable source. */ g_cancellable_cancel (cancellable); - g_object_unref (cancellable); + /* This thread can’t drop its reference to the #GCancellable here, as it + * might not be the final reference (depending on how the race is + * resolved: #GCancellableSource holds a strong ref on the #GCancellable), + * and at this point we can’t guarantee to support disposing of a + * #GCancellable in a different thread from where it’s created, especially + * when signal handlers are connected to it. + * + * So this is a workaround for a disposal-in-another-thread bug for + * #GCancellable, but there’s no hope of debugging and resolving it with + * this test setup, and the bug is orthogonal to what’s being tested here + * (a race between #GCancellable and #GCancellableSource). */ + g_ptr_array_add (cancellables_pending_unref, g_steal_pointer (&cancellable)); } /* Indicate that the test has finished. Can’t use %NULL as #GAsyncQueue @@ -322,6 +324,8 @@ test_cancellable_source_threaded_dispose (void) g_async_queue_unref (data.cancellable_source_queue); g_mutex_clear (&data.mutex); g_cond_clear (&data.cond); + + g_ptr_array_unref (cancellables_pending_unref); } int