win32: make gio stream cancellable
[platform/upstream/glib.git] / gio / gasynchelper.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
25 #include "gasynchelper.h"
26
27
28 /*< private >
29  * SECTION:gasynchelper
30  * @short_description: Asynchronous Helper Functions
31  * @include: gio/gio.h
32  * @see_also: #GAsyncResult
33  * 
34  * Provides helper functions for asynchronous operations.
35  *
36  **/
37
38 /*************************************************************************
39  *             fd source                                                 *
40  ************************************************************************/
41
42 typedef struct 
43 {
44   GSource source;
45   GPollFD pollfd;
46 } FDSource;
47
48 static gboolean 
49 fd_source_prepare (GSource *source,
50                    gint    *timeout)
51 {
52   *timeout = -1;
53   return FALSE;
54 }
55
56 static gboolean 
57 fd_source_check (GSource *source)
58 {
59   FDSource *fd_source = (FDSource *)source;
60
61   return fd_source->pollfd.revents != 0;
62 }
63
64 static gboolean
65 fd_source_dispatch (GSource     *source,
66                     GSourceFunc  callback,
67                     gpointer     user_data)
68
69 {
70   GFDSourceFunc func = (GFDSourceFunc)callback;
71   FDSource *fd_source = (FDSource *)source;
72
73   g_warn_if_fail (func != NULL);
74
75   return (*func) (fd_source->pollfd.fd, fd_source->pollfd.revents, user_data);
76 }
77
78 static void 
79 fd_source_finalize (GSource *source)
80 {
81 }
82
83 static gboolean
84 fd_source_closure_callback (int           fd,
85                             GIOCondition  condition,
86                             gpointer      data)
87 {
88   GClosure *closure = data;
89
90   GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
91   GValue result_value = G_VALUE_INIT;
92   gboolean result;
93
94   g_value_init (&result_value, G_TYPE_BOOLEAN);
95
96   g_value_init (&params[0], G_TYPE_INT);
97   g_value_set_int (&params[0], fd);
98
99   g_value_init (&params[1], G_TYPE_IO_CONDITION);
100   g_value_set_flags (&params[1], condition);
101
102   g_closure_invoke (closure, &result_value, 2, params, NULL);
103
104   result = g_value_get_boolean (&result_value);
105   g_value_unset (&result_value);
106   g_value_unset (&params[0]);
107   g_value_unset (&params[1]);
108
109   return result;
110 }
111
112 static void
113 fd_source_closure_marshal (GClosure     *closure,
114                            GValue       *return_value,
115                            guint         n_param_values,
116                            const GValue *param_values,
117                            gpointer      invocation_hint,
118                            gpointer      marshal_data)
119 {
120   GFDSourceFunc callback;
121   GCClosure *cc = (GCClosure*) closure;
122   gboolean v_return;
123
124   g_return_if_fail (return_value != NULL);
125   g_return_if_fail (n_param_values == 0);
126
127   callback = (GFDSourceFunc) (marshal_data ? marshal_data : cc->callback);
128
129   v_return = callback (g_value_get_int (param_values),
130                        g_value_get_flags (param_values + 1),
131                        closure->data);
132
133   g_value_set_boolean (return_value, v_return);
134 }
135
136 static GSourceFuncs fd_source_funcs = {
137   fd_source_prepare,
138   fd_source_check,
139   fd_source_dispatch,
140   fd_source_finalize,
141   (GSourceFunc)fd_source_closure_callback,
142   (GSourceDummyMarshal)fd_source_closure_marshal,
143 };
144
145 GSource *
146 _g_fd_source_new (int           fd,
147                   gushort       events,
148                   GCancellable *cancellable)
149 {
150   GSource *source;
151   FDSource *fd_source;
152
153   source = g_source_new (&fd_source_funcs, sizeof (FDSource));
154   fd_source = (FDSource *)source;
155
156   fd_source->pollfd.fd = fd;
157   fd_source->pollfd.events = events;
158   g_source_add_poll (source, &fd_source->pollfd);
159
160   if (cancellable)
161     {
162       GSource *cancellable_source = g_cancellable_source_new (cancellable);
163
164       g_source_set_dummy_callback (cancellable_source);
165       g_source_add_child_source (source, cancellable_source);
166       g_source_unref (cancellable_source);
167     }
168
169   return source;
170 }
171
172 #ifdef G_OS_WIN32
173 gboolean
174 _g_win32_overlap_wait_result (HANDLE           hfile,
175                               OVERLAPPED      *overlap,
176                               DWORD           *transferred,
177                               GCancellable    *cancellable)
178 {
179   GPollFD pollfd[2];
180   gboolean result = FALSE;
181   gint num, npoll;
182
183   pollfd[0].fd = (gint)overlap->hEvent;
184   pollfd[0].events = G_IO_IN;
185   num = 1;
186
187   if (g_cancellable_make_pollfd (cancellable, &pollfd[1]))
188     num++;
189
190 loop:
191   npoll = g_poll (pollfd, num, -1);
192   if (npoll <= 0)
193     /* error out, should never happen */
194     goto end;
195
196   if (g_cancellable_is_cancelled (cancellable))
197     {
198       /* CancelIO only cancels pending operations issued by the
199        * current thread and since we're doing only sync operations,
200        * this is safe.... */
201       /* CancelIoEx is only Vista+. Since we have only one overlap
202        * operaton on this thread, we can just use: */
203       result = CancelIo (hfile);
204       g_warn_if_fail (result);
205     }
206
207   result = GetOverlappedResult (overlap->hEvent, overlap, transferred, FALSE);
208   if (result == FALSE &&
209       GetLastError () == ERROR_IO_INCOMPLETE &&
210       !g_cancellable_is_cancelled (cancellable))
211     goto loop;
212
213 end:
214   if (num > 1)
215     g_cancellable_release_fd (cancellable);
216
217   return result;
218 }
219 #endif