Merge branch 'cancellable-test-debugging' into 'master'

tests: Fix intermittent failure in GCancellableSource test

Closes #1764

See merge request GNOME/glib!1539
This commit is contained in:
Philip Withnall 2020-06-25 13:02:18 +00:00
commit 582ffcd6ea

View File

@ -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 cant 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 cant guarantee to support disposing of a
* #GCancellable in a different thread from where its created, especially
* when signal handlers are connected to it.
*
* So this is a workaround for a disposal-in-another-thread bug for
* #GCancellable, but theres no hope of debugging and resolving it with
* this test setup, and the bug is orthogonal to whats 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. Cant 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