1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2011 Collabora Ltd.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Stef Walter <stefw@collabora.co.uk>
19 */
20
21 #include <locale.h>
22
23 #include <gio/gio.h>
24
25 /* How long to wait in ms for each iteration */
26 #define WAIT_ITERATION (10)
27
28 static gint num_async_operations = 0;
29
30 typedef struct
31 {
32 guint iterations_requested;
33 guint iterations_done;
34 } MockOperationData;
35
36 static void
mock_operation_free(gpointer user_data)37 mock_operation_free (gpointer user_data)
38 {
39 MockOperationData *data = user_data;
40 g_free (data);
41 }
42
43 static void
mock_operation_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)44 mock_operation_thread (GTask *task,
45 gpointer source_object,
46 gpointer task_data,
47 GCancellable *cancellable)
48 {
49 MockOperationData *data = task_data;
50 guint i;
51
52 for (i = 0; i < data->iterations_requested; i++)
53 {
54 if (g_cancellable_is_cancelled (cancellable))
55 break;
56 if (g_test_verbose ())
57 g_printerr ("THRD: %u iteration %u\n", data->iterations_requested, i);
58 g_usleep (WAIT_ITERATION * 1000);
59 }
60
61 if (g_test_verbose ())
62 g_printerr ("THRD: %u stopped at %u\n", data->iterations_requested, i);
63 data->iterations_done = i;
64
65 g_task_return_boolean (task, TRUE);
66 }
67
68 static gboolean
mock_operation_timeout(gpointer user_data)69 mock_operation_timeout (gpointer user_data)
70 {
71 GTask *task;
72 MockOperationData *data;
73 gboolean done = FALSE;
74
75 task = G_TASK (user_data);
76 data = g_task_get_task_data (task);
77
78 if (data->iterations_done >= data->iterations_requested)
79 done = TRUE;
80
81 if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
82 done = TRUE;
83
84 if (done)
85 {
86 if (g_test_verbose ())
87 g_printerr ("LOOP: %u stopped at %u\n", data->iterations_requested,\
88 data->iterations_done);
89 g_task_return_boolean (task, TRUE);
90 return FALSE; /* don't call timeout again */
91 }
92 else
93 {
94 data->iterations_done++;
95 if (g_test_verbose ())
96 g_printerr ("LOOP: %u iteration %u\n", data->iterations_requested,
97 data->iterations_done);
98 return TRUE; /* call timeout */
99 }
100 }
101
102 static void
mock_operation_async(guint wait_iterations,gboolean run_in_thread,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)103 mock_operation_async (guint wait_iterations,
104 gboolean run_in_thread,
105 GCancellable *cancellable,
106 GAsyncReadyCallback callback,
107 gpointer user_data)
108 {
109 GTask *task;
110 MockOperationData *data;
111
112 task = g_task_new (NULL, cancellable, callback, user_data);
113 data = g_new0 (MockOperationData, 1);
114 data->iterations_requested = wait_iterations;
115 g_task_set_task_data (task, data, mock_operation_free);
116
117 if (run_in_thread)
118 {
119 g_task_run_in_thread (task, mock_operation_thread);
120 if (g_test_verbose ())
121 g_printerr ("THRD: %d started\n", wait_iterations);
122 }
123 else
124 {
125 g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
126 g_object_ref (task), g_object_unref);
127 if (g_test_verbose ())
128 g_printerr ("LOOP: %d started\n", wait_iterations);
129 }
130
131 g_object_unref (task);
132 }
133
134 static guint
mock_operation_finish(GAsyncResult * result,GError ** error)135 mock_operation_finish (GAsyncResult *result,
136 GError **error)
137 {
138 MockOperationData *data;
139 GTask *task;
140
141 g_assert (g_task_is_valid (result, NULL));
142
143 /* This test expects the return value to be iterations_done even
144 * when an error is set.
145 */
146 task = G_TASK (result);
147 data = g_task_get_task_data (task);
148
149 g_task_propagate_boolean (task, error);
150 return data->iterations_done;
151 }
152
153 GMainLoop *loop;
154
155 static void
on_mock_operation_ready(GObject * source,GAsyncResult * result,gpointer user_data)156 on_mock_operation_ready (GObject *source,
157 GAsyncResult *result,
158 gpointer user_data)
159 {
160 guint iterations_requested;
161 guint iterations_done;
162 GError *error = NULL;
163
164 iterations_requested = GPOINTER_TO_UINT (user_data);
165 iterations_done = mock_operation_finish (result, &error);
166
167 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
168 g_error_free (error);
169
170 g_assert_cmpint (iterations_requested, >, iterations_done);
171 num_async_operations--;
172
173 if (!num_async_operations)
174 g_main_loop_quit (loop);
175 }
176
177 static gboolean
on_main_loop_timeout_quit(gpointer user_data)178 on_main_loop_timeout_quit (gpointer user_data)
179 {
180 GMainLoop *loop = user_data;
181 g_main_loop_quit (loop);
182 return FALSE;
183 }
184
185 static void
test_cancel_multiple_concurrent(void)186 test_cancel_multiple_concurrent (void)
187 {
188 GCancellable *cancellable;
189 guint i, iterations;
190
191 cancellable = g_cancellable_new ();
192 loop = g_main_loop_new (NULL, FALSE);
193
194 for (i = 0; i < 45; i++)
195 {
196 iterations = i + 10;
197 mock_operation_async (iterations, g_random_boolean (), cancellable,
198 on_mock_operation_ready, GUINT_TO_POINTER (iterations));
199 num_async_operations++;
200 }
201
202 /* Wait for two iterations, to give threads a chance to start up */
203 g_timeout_add (WAIT_ITERATION * 2, on_main_loop_timeout_quit, loop);
204 g_main_loop_run (loop);
205 g_assert_cmpint (num_async_operations, ==, 45);
206 if (g_test_verbose ())
207 g_printerr ("CANCEL: %d operations\n", num_async_operations);
208 g_cancellable_cancel (cancellable);
209 g_assert (g_cancellable_is_cancelled (cancellable));
210
211 /* Wait for all operations to be cancelled */
212 g_main_loop_run (loop);
213 g_assert_cmpint (num_async_operations, ==, 0);
214
215 g_object_unref (cancellable);
216 g_main_loop_unref (loop);
217 }
218
219 static void
test_cancel_null(void)220 test_cancel_null (void)
221 {
222 g_cancellable_cancel (NULL);
223 }
224
225 int
main(int argc,char * argv[])226 main (int argc, char *argv[])
227 {
228 g_test_init (&argc, &argv, NULL);
229
230 g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent);
231 g_test_add_func ("/cancellable/null", test_cancel_null);
232
233 return g_test_run ();
234 }
235