--- /dev/null
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include <locale.h>
+
+#include <gio/gio.h>
+
+/* How long to wait in ms for each iteration */
+#define WAIT_ITERATION (10)
+
+static gint num_async_operations = 0;
+
+typedef struct
+{
+ guint iterations_requested;
+ guint iterations_done;
+ GCancellable *cancellable;
+} MockOperationData;
+
+static void
+mock_operation_free (gpointer user_data)
+{
+ MockOperationData *data = user_data;
+ g_object_unref (data->cancellable);
+ g_free (data);
+}
+
+static void
+mock_operation_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ MockOperationData *data;
+ guint i;
+
+ data = g_simple_async_result_get_op_res_gpointer (simple);
+ g_assert (data->cancellable == cancellable);
+
+ for (i = 0; i < data->iterations_requested; i++)
+ {
+ if (g_cancellable_is_cancelled (data->cancellable))
+ break;
+ if (g_test_verbose ())
+ g_printerr ("THRD: %u iteration %u\n", 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;
+}
+
+static gboolean
+mock_operation_timeout (gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ MockOperationData *data;
+ GError *error = NULL;
+ gboolean done = FALSE;
+
+ simple = G_SIMPLE_ASYNC_RESULT (user_data);
+ data = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (data->iterations_done >= data->iterations_requested)
+ done = TRUE;
+
+ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error)) {
+ g_simple_async_result_take_error (simple, error);
+ done = TRUE;
+ }
+
+ if (done) {
+ if (g_test_verbose ())
+ g_printerr ("LOOP: %u stopped at %u\n", data->iterations_requested,\
+ data->iterations_done);
+ g_simple_async_result_complete (simple);
+ return FALSE; /* don't call timeout again */
+
+ } else {
+ data->iterations_done++;
+ if (g_test_verbose ())
+ g_printerr ("LOOP: %u iteration %u\n", data->iterations_requested,
+ data->iterations_done);
+ return TRUE; /* call timeout */
+ }
+}
+
+static void
+mock_operation_async (guint wait_iterations,
+ gboolean run_in_thread,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ MockOperationData *data;
+
+ simple = g_simple_async_result_new (NULL, callback, user_data,
+ mock_operation_async);
+ data = g_new0 (MockOperationData, 1);
+ data->iterations_requested = wait_iterations;
+ data->cancellable = g_object_ref (cancellable);
+ g_simple_async_result_set_op_res_gpointer (simple, data, mock_operation_free);
+
+ if (run_in_thread) {
+ g_simple_async_result_run_in_thread (simple, mock_operation_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+ if (g_test_verbose ())
+ g_printerr ("THRD: %d started\n", wait_iterations);
+ } else {
+ g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
+ g_object_ref (simple), g_object_unref);
+ if (g_test_verbose ())
+ g_printerr ("LOOP: %d started\n", wait_iterations);
+ }
+
+ g_object_unref (simple);
+}
+
+static guint
+mock_operation_finish (GAsyncResult *result,
+ GError **error)
+{
+ MockOperationData *data;
+
+ g_assert (g_simple_async_result_is_valid (result, NULL, mock_operation_async));
+ g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+
+ data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+ return data->iterations_done;
+}
+
+static void
+on_mock_operation_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ guint iterations_requested;
+ guint iterations_done;
+ GError *error = NULL;
+
+ iterations_requested = GPOINTER_TO_UINT (user_data);
+ iterations_done = mock_operation_finish (result, &error);
+
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_error_free (error);
+
+ g_assert_cmpint (iterations_requested, >, iterations_done);
+ num_async_operations--;
+}
+
+static gboolean
+on_main_loop_timeout_quit (gpointer user_data)
+{
+ GMainLoop *loop = user_data;
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+test_cancel_multiple_concurrent (void)
+{
+ GCancellable *cancellable;
+ guint i, iterations;
+ GMainLoop *loop;
+
+ cancellable = g_cancellable_new ();
+ loop = g_main_loop_new (NULL, FALSE);
+
+ for (i = 0; i < 45; i++)
+ {
+ iterations = i + 10;
+ mock_operation_async (iterations, g_random_boolean (), cancellable,
+ on_mock_operation_ready, GUINT_TO_POINTER (iterations));
+ 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);
+ if (g_test_verbose ())
+ g_printerr ("CANCEL: %d operations\n", num_async_operations);
+ g_cancellable_cancel (cancellable);
+ g_assert (g_cancellable_is_cancelled (cancellable));
+
+ /* Wait for two more iterations, and all threads should be cancelled */
+ g_timeout_add (WAIT_ITERATION * 2, on_main_loop_timeout_quit, loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (num_async_operations, ==, 0);
+
+ g_object_unref (cancellable);
+ g_main_loop_unref (loop);
+}
+
+int
+main (int argc, char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent);
+
+ return g_test_run ();
+}