cancellable.c 6.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/* 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;
} MockOperationData;

static void
mock_operation_free (gpointer user_data)
{
  MockOperationData *data = user_data;
  g_free (data);
}

static void
46 47 48 49
mock_operation_thread (GTask        *task,
                       gpointer      source_object,
                       gpointer      task_data,
                       GCancellable *cancellable)
50
{
51
  MockOperationData *data = task_data;
52 53 54 55
  guint i;

  for (i = 0; i < data->iterations_requested; i++)
    {
56
      if (g_cancellable_is_cancelled (cancellable))
57 58 59 60 61 62 63 64 65
        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;
66 67

  g_task_return_boolean (task, TRUE);
68 69 70 71 72
}

static gboolean
mock_operation_timeout (gpointer user_data)
{
73
  GTask *task;
74 75 76
  MockOperationData *data;
  gboolean done = FALSE;

77 78
  task = G_TASK (user_data);
  data = g_task_get_task_data (task);
79 80 81 82

  if (data->iterations_done >= data->iterations_requested)
      done = TRUE;

83
  if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
84 85
      done = TRUE;

86 87
  if (done)
    {
88 89 90
      if (g_test_verbose ())
        g_printerr ("LOOP: %u stopped at %u\n", data->iterations_requested,\
                    data->iterations_done);
91
      g_task_return_boolean (task, TRUE);
92
      return FALSE; /* don't call timeout again */
93 94 95
    }
  else
    {
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
      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)
{
111
  GTask *task;
112 113
  MockOperationData *data;

114
  task = g_task_new (NULL, cancellable, callback, user_data);
115 116
  data = g_new0 (MockOperationData, 1);
  data->iterations_requested = wait_iterations;
117
  g_task_set_task_data (task, data, mock_operation_free);
118

119 120 121
  if (run_in_thread)
    {
      g_task_run_in_thread (task, mock_operation_thread);
122 123
      if (g_test_verbose ())
        g_printerr ("THRD: %d started\n", wait_iterations);
124 125 126
    }
  else
    {
127
      g_timeout_add_full (G_PRIORITY_DEFAULT, WAIT_ITERATION, mock_operation_timeout,
128
                          g_object_ref (task), g_object_unref);
129 130
      if (g_test_verbose ())
        g_printerr ("LOOP: %d started\n", wait_iterations);
131
    }
132

133
  g_object_unref (task);
134 135 136 137 138 139 140
}

static guint
mock_operation_finish (GAsyncResult  *result,
                       GError       **error)
{
  MockOperationData *data;
141 142 143
  GTask *task;

  g_assert (g_task_is_valid (result, NULL));
144

145 146 147 148 149
  /* This test expects the return value to be iterations_done even
   * when an error is set.
   */
  task = G_TASK (result);
  data = g_task_get_task_data (task);
150

151
  g_task_propagate_boolean (task, error);
152 153 154
  return data->iterations_done;
}

155 156
GMainLoop *loop;

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
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--;
174 175 176

  if (!num_async_operations)
    g_main_loop_quit (loop);
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
}

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;

  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));

213
  /* Wait for all operations to be cancelled */
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
  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_test_init (&argc, &argv, NULL);

  g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent);

  return g_test_run ();
}