gcancellable.c revision 43ae3892110d4d4a0c744a10cbcbdcc337efefc1
1/* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
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 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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23#include <config.h>
24#ifdef HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27#include <fcntl.h>
28#include <gioerror.h>
29#ifdef G_OS_WIN32
30#include <io.h>
31#ifndef pipe
32#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
33#endif
34#endif
35#include "gcancellable.h"
36#include "glibintl.h"
37
38#include "gioalias.h"
39
40/**
41 * SECTION:gcancellable
42 * @short_description: Thread-safe Operation Cancellation Stack
43 * @include: gio/gcancellable.h
44 *
45 * GCancellable is a thread-safe operation cancellation stack used
46 * throughout GIO to allow for cancellation of asynchronous operations.
47 */
48
49enum {
50  CANCELLED,
51  LAST_SIGNAL
52};
53
54struct _GCancellable
55{
56  GObject parent_instance;
57
58  guint cancelled : 1;
59  guint allocated_pipe : 1;
60  int cancel_pipe[2];
61};
62
63static guint signals[LAST_SIGNAL] = { 0 };
64
65G_DEFINE_TYPE (GCancellable, g_cancellable, G_TYPE_OBJECT);
66
67static GStaticPrivate current_cancellable = G_STATIC_PRIVATE_INIT;
68G_LOCK_DEFINE_STATIC(cancellable);
69
70static void
71g_cancellable_finalize (GObject *object)
72{
73  GCancellable *cancellable = G_CANCELLABLE (object);
74
75  if (cancellable->cancel_pipe[0] != -1)
76    close (cancellable->cancel_pipe[0]);
77
78  if (cancellable->cancel_pipe[1] != -1)
79    close (cancellable->cancel_pipe[1]);
80
81  if (G_OBJECT_CLASS (g_cancellable_parent_class)->finalize)
82    (*G_OBJECT_CLASS (g_cancellable_parent_class)->finalize) (object);
83}
84
85static void
86g_cancellable_class_init (GCancellableClass *klass)
87{
88  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
89
90  gobject_class->finalize = g_cancellable_finalize;
91
92  /**
93   * GCancellable::cancelled:
94   * @cancellable: a #GCancellable.
95   *
96   * Emitted when the operation has been cancelled from another thread.
97   */
98  signals[CANCELLED] =
99    g_signal_new (I_("cancelled"),
100		  G_TYPE_FROM_CLASS (gobject_class),
101		  G_SIGNAL_RUN_LAST,
102		  G_STRUCT_OFFSET (GCancellableClass, cancelled),
103		  NULL, NULL,
104		  g_cclosure_marshal_VOID__VOID,
105		  G_TYPE_NONE, 0);
106
107}
108
109static void
110set_fd_nonblocking (int fd)
111{
112#ifdef F_GETFL
113  glong fcntl_flags;
114  fcntl_flags = fcntl (fd, F_GETFL);
115
116#ifdef O_NONBLOCK
117  fcntl_flags |= O_NONBLOCK;
118#else
119  fcntl_flags |= O_NDELAY;
120#endif
121
122  fcntl (fd, F_SETFL, fcntl_flags);
123#endif
124}
125
126static void
127g_cancellable_open_pipe (GCancellable *cancellable)
128{
129  if (pipe (cancellable->cancel_pipe) == 0)
130    {
131      /* Make them nonblocking, just to be sure we don't block
132       * on errors and stuff
133       */
134      set_fd_nonblocking (cancellable->cancel_pipe[0]);
135      set_fd_nonblocking (cancellable->cancel_pipe[1]);
136    }
137  else
138    g_warning ("Failed to create pipe for GCancellable. Out of file descriptors?");
139}
140
141static void
142g_cancellable_init (GCancellable *cancellable)
143{
144  cancellable->cancel_pipe[0] = -1;
145  cancellable->cancel_pipe[1] = -1;
146}
147
148/**
149 * g_cancellable_new:
150 *
151 * Creates a new #GCancellable object.
152 *
153 * Returns: a #GCancellable.
154 **/
155GCancellable *
156g_cancellable_new (void)
157{
158  return g_object_new (G_TYPE_CANCELLABLE, NULL);
159}
160
161/**
162 * g_push_current_cancellable:
163 * @cancellable: optional #GCancellable object, %NULL to ignore.
164 *
165 * Pushes @cancellable onto the cancellable stack.
166 **/
167void
168g_push_current_cancellable (GCancellable *cancellable)
169{
170  GSList *l;
171
172  g_assert (cancellable != NULL);
173
174  l = g_static_private_get (&current_cancellable);
175  l = g_slist_prepend (l, cancellable);
176  g_static_private_set (&current_cancellable, l, NULL);
177}
178
179/**
180 * g_pop_current_cancellable:
181 * @cancellable: optional #GCancellable object, %NULL to ignore.
182 *
183 * Pops @cancellable off the cancellable stack if @cancellable
184 * is on the top of the stack.
185 **/
186void
187g_pop_current_cancellable (GCancellable *cancellable)
188{
189  GSList *l;
190
191  l = g_static_private_get (&current_cancellable);
192
193  g_assert (l != NULL);
194  g_assert (l->data == cancellable);
195
196  l = g_slist_delete_link (l, l);
197  g_static_private_set (&current_cancellable, l, NULL);
198}
199
200/**
201 * g_cancellable_get_current:
202 *
203 * Gets the top cancellable from the stack.
204 *
205 * Returns: a #GCancellable from the top of the stack, or %NULL
206 * if the stack is empty.
207 **/
208GCancellable *
209g_cancellable_get_current  (void)
210{
211  GSList *l;
212
213  l = g_static_private_get (&current_cancellable);
214  if (l == NULL)
215    return NULL;
216
217  return G_CANCELLABLE (l->data);
218}
219
220/**
221 * g_cancellable_reset:
222 * @cancellable: a #GCancellable object.
223 *
224 * Resets @cancellable to its uncancelled state.
225 **/
226void
227g_cancellable_reset (GCancellable *cancellable)
228{
229  g_return_if_fail (G_IS_CANCELLABLE (cancellable));
230
231  G_LOCK(cancellable);
232  /* Make sure we're not leaving old cancel state around */
233  if (cancellable->cancelled)
234    {
235      char ch;
236      if (cancellable->cancel_pipe[0] != -1)
237	read (cancellable->cancel_pipe[0], &ch, 1);
238      cancellable->cancelled = FALSE;
239    }
240  G_UNLOCK(cancellable);
241}
242
243/**
244 * g_cancellable_is_cancelled:
245 * @cancellable: a #GCancellable or NULL.
246 *
247 * Checks if a cancellable job has been cancelled.
248 *
249 * Returns: %TRUE if @cancellable is is cancelled,
250 * FALSE if called with %NULL or if item is not cancelled.
251 **/
252gboolean
253g_cancellable_is_cancelled (GCancellable *cancellable)
254{
255  return cancellable != NULL && cancellable->cancelled;
256}
257
258/**
259 * g_cancellable_set_error_if_cancelled:
260 * @cancellable: a #GCancellable object.
261 * @error: #GError to append error state to.
262 *
263 * Sets the current error to notify that the operation was cancelled.
264 *
265 * Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not.
266 **/
267gboolean
268g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
269				      GError       **error)
270{
271  if (g_cancellable_is_cancelled (cancellable))
272    {
273      g_set_error (error,
274		   G_IO_ERROR,
275		   G_IO_ERROR_CANCELLED,
276		   _("Operation was cancelled"));
277      return TRUE;
278    }
279
280  return FALSE;
281}
282
283/**
284 * g_cancellable_get_fd:
285 * @cancellable: a #GCancellable.
286 *
287 * Gets the file descriptor for a cancellable job.
288 *
289 * Returns: A valid file descriptor. %-1 if the file descriptor
290 * is not supported, or on errors.
291 **/
292int
293g_cancellable_get_fd (GCancellable *cancellable)
294{
295  int fd;
296  if (cancellable == NULL)
297    return -1;
298
299  G_LOCK(cancellable);
300  if (!cancellable->allocated_pipe)
301    {
302      cancellable->allocated_pipe = TRUE;
303      g_cancellable_open_pipe (cancellable);
304    }
305
306  fd = cancellable->cancel_pipe[0];
307  G_UNLOCK(cancellable);
308
309  return fd;
310}
311
312/**
313 * g_cancellable_cancel:
314 * @cancellable: a #GCancellable object.
315 *
316 * Will set @cancellable to cancelled, and will emit the CANCELLED
317 * signal. This function is thread-safe.
318 **/
319void
320g_cancellable_cancel (GCancellable *cancellable)
321{
322  gboolean cancel;
323
324  cancel = FALSE;
325
326  G_LOCK(cancellable);
327  if (cancellable != NULL &&
328      !cancellable->cancelled)
329    {
330      char ch = 'x';
331      cancel = TRUE;
332      cancellable->cancelled = TRUE;
333      if (cancellable->cancel_pipe[1] != -1)
334	write (cancellable->cancel_pipe[1], &ch, 1);
335    }
336  G_UNLOCK(cancellable);
337
338  if (cancel)
339    {
340      g_object_ref (cancellable);
341      g_signal_emit (cancellable, signals[CANCELLED], 0);
342      g_object_unref (cancellable);
343    }
344}
345
346#define __G_CANCELLABLE_C__
347#include "gioaliasdef.c"
348