• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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