mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-10 03:04:05 +02:00
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:
@@ -29,8 +29,8 @@ static gint num_async_operations = 0;
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
guint iterations_requested;
|
guint iterations_requested; /* construct-only */
|
||||||
guint iterations_done;
|
guint iterations_done; /* (atomic) */
|
||||||
} MockOperationData;
|
} MockOperationData;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -54,13 +54,13 @@ mock_operation_thread (GTask *task,
|
|||||||
if (g_cancellable_is_cancelled (cancellable))
|
if (g_cancellable_is_cancelled (cancellable))
|
||||||
break;
|
break;
|
||||||
if (g_test_verbose ())
|
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);
|
g_usleep (WAIT_ITERATION * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_test_verbose ())
|
if (g_test_verbose ())
|
||||||
g_printerr ("THRD: %u stopped at %u\n", data->iterations_requested, i);
|
g_test_message ("THRD: %u stopped at %u", data->iterations_requested, i);
|
||||||
data->iterations_done = i;
|
g_atomic_int_add (&data->iterations_done, i);
|
||||||
|
|
||||||
g_task_return_boolean (task, TRUE);
|
g_task_return_boolean (task, TRUE);
|
||||||
}
|
}
|
||||||
@@ -71,11 +71,13 @@ mock_operation_timeout (gpointer user_data)
|
|||||||
GTask *task;
|
GTask *task;
|
||||||
MockOperationData *data;
|
MockOperationData *data;
|
||||||
gboolean done = FALSE;
|
gboolean done = FALSE;
|
||||||
|
guint iterations_done;
|
||||||
|
|
||||||
task = G_TASK (user_data);
|
task = G_TASK (user_data);
|
||||||
data = g_task_get_task_data (task);
|
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;
|
done = TRUE;
|
||||||
|
|
||||||
if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
|
if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
|
||||||
@@ -84,18 +86,18 @@ mock_operation_timeout (gpointer user_data)
|
|||||||
if (done)
|
if (done)
|
||||||
{
|
{
|
||||||
if (g_test_verbose ())
|
if (g_test_verbose ())
|
||||||
g_printerr ("LOOP: %u stopped at %u\n", data->iterations_requested,\
|
g_test_message ("LOOP: %u stopped at %u",
|
||||||
data->iterations_done);
|
data->iterations_requested, iterations_done);
|
||||||
g_task_return_boolean (task, TRUE);
|
g_task_return_boolean (task, TRUE);
|
||||||
return FALSE; /* don't call timeout again */
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
data->iterations_done++;
|
g_atomic_int_inc (&data->iterations_done);
|
||||||
if (g_test_verbose ())
|
if (g_test_verbose ())
|
||||||
g_printerr ("LOOP: %u iteration %u\n", data->iterations_requested,
|
g_test_message ("LOOP: %u iteration %u",
|
||||||
data->iterations_done);
|
data->iterations_requested, iterations_done + 1);
|
||||||
return TRUE; /* call timeout */
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,14 +120,14 @@ mock_operation_async (guint wait_iterations,
|
|||||||
{
|
{
|
||||||
g_task_run_in_thread (task, mock_operation_thread);
|
g_task_run_in_thread (task, mock_operation_thread);
|
||||||
if (g_test_verbose ())
|
if (g_test_verbose ())
|
||||||
g_printerr ("THRD: %d started\n", wait_iterations);
|
g_test_message ("THRD: %d started", wait_iterations);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
|
g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
|
||||||
g_object_ref (task), g_object_unref);
|
g_object_ref (task), g_object_unref);
|
||||||
if (g_test_verbose ())
|
if (g_test_verbose ())
|
||||||
g_printerr ("LOOP: %d started\n", wait_iterations);
|
g_test_message ("LOOP: %d started", wait_iterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref (task);
|
g_object_unref (task);
|
||||||
@@ -147,11 +149,9 @@ mock_operation_finish (GAsyncResult *result,
|
|||||||
data = g_task_get_task_data (task);
|
data = g_task_get_task_data (task);
|
||||||
|
|
||||||
g_task_propagate_boolean (task, error);
|
g_task_propagate_boolean (task, error);
|
||||||
return data->iterations_done;
|
return g_atomic_int_get (&data->iterations_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
GMainLoop *loop;
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_mock_operation_ready (GObject *source,
|
on_mock_operation_ready (GObject *source,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
@@ -169,17 +169,7 @@ on_mock_operation_ready (GObject *source,
|
|||||||
|
|
||||||
g_assert_cmpint (iterations_requested, >, iterations_done);
|
g_assert_cmpint (iterations_requested, >, iterations_done);
|
||||||
num_async_operations--;
|
num_async_operations--;
|
||||||
|
g_main_context_wakeup (NULL);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -195,7 +185,6 @@ test_cancel_multiple_concurrent (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancellable = g_cancellable_new ();
|
cancellable = g_cancellable_new ();
|
||||||
loop = g_main_loop_new (NULL, FALSE);
|
|
||||||
|
|
||||||
for (i = 0; i < 45; i++)
|
for (i = 0; i < 45; i++)
|
||||||
{
|
{
|
||||||
@@ -205,21 +194,22 @@ test_cancel_multiple_concurrent (void)
|
|||||||
num_async_operations++;
|
num_async_operations++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for two iterations, to give threads a chance to start up */
|
/* Wait for the threads to start up */
|
||||||
g_timeout_add (WAIT_ITERATION * 2, on_main_loop_timeout_quit, loop);
|
while (num_async_operations != 45)
|
||||||
g_main_loop_run (loop);
|
g_main_context_iteration (NULL, TRUE);
|
||||||
g_assert_cmpint (num_async_operations, ==, 45);
|
g_assert_cmpint (num_async_operations, ==, 45);\
|
||||||
|
|
||||||
if (g_test_verbose ())
|
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_cancellable_cancel (cancellable);
|
||||||
g_assert_true (g_cancellable_is_cancelled (cancellable));
|
g_assert_true (g_cancellable_is_cancelled (cancellable));
|
||||||
|
|
||||||
/* Wait for all operations to be cancelled */
|
/* 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_assert_cmpint (num_async_operations, ==, 0);
|
||||||
|
|
||||||
g_object_unref (cancellable);
|
g_object_unref (cancellable);
|
||||||
g_main_loop_unref (loop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -270,6 +260,7 @@ test_cancellable_source_threaded_dispose (void)
|
|||||||
ThreadedDisposeData data;
|
ThreadedDisposeData data;
|
||||||
GThread *thread = NULL;
|
GThread *thread = NULL;
|
||||||
guint i;
|
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 "
|
g_test_summary ("Test a thread race between disposing of a GCancellableSource "
|
||||||
"(in one thread) and cancelling the GCancellable it refers "
|
"(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. */
|
/* Race with disposal of the cancellable source. */
|
||||||
g_cancellable_cancel (cancellable);
|
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
|
/* 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_async_queue_unref (data.cancellable_source_queue);
|
||||||
g_mutex_clear (&data.mutex);
|
g_mutex_clear (&data.mutex);
|
||||||
g_cond_clear (&data.cond);
|
g_cond_clear (&data.cond);
|
||||||
|
|
||||||
|
g_ptr_array_unref (cancellables_pending_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
Reference in New Issue
Block a user