http://mail.gnome.org/archives/gtk-devel-list/2007-October/msg00089.html
[platform/upstream/glib.git] / gio / gcancellable.c
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
49 enum {
50   CANCELLED,
51   LAST_SIGNAL
52 };
53
54 struct _GCancellable
55 {
56   GObject parent_instance;
57
58   guint cancelled : 1;
59   guint allocated_pipe : 1;
60   int cancel_pipe[2];
61 };
62
63 static guint signals[LAST_SIGNAL] = { 0 };
64
65 G_DEFINE_TYPE (GCancellable, g_cancellable, G_TYPE_OBJECT);
66
67 static GStaticPrivate current_cancellable = G_STATIC_PRIVATE_INIT;
68 G_LOCK_DEFINE_STATIC(cancellable);
69   
70 static void
71 g_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
85 static void
86 g_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
109 static void
110 set_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
126 static void
127 g_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
141 static void
142 g_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  **/
155 GCancellable *
156 g_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  **/
167 void
168 g_push_current_cancellable (GCancellable *cancellable)
169 {
170   GSList *l;
171
172   g_return_if_fail (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  **/
186 void
187 g_pop_current_cancellable (GCancellable *cancellable)
188 {
189   GSList *l;
190   
191   l = g_static_private_get (&current_cancellable);
192   
193   g_return_if_fail (l != NULL);
194   g_return_if_fail (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  **/
208 GCancellable *
209 g_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  **/
226 void 
227 g_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  **/
252 gboolean
253 g_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  **/
267 gboolean
268 g_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  **/
292 int
293 g_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  **/
319 void
320 g_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"