gio: GCancellable can be used concurrently
[platform/upstream/glib.git] / gio / gwin32resolver.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4  * 
5  * Copyright (C) 2008 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24 #include <glib.h>
25 #include "glibintl.h"
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "gwin32resolver.h"
31 #include "gnetworkingprivate.h"
32
33 #include "gcancellable.h"
34 #include "gsimpleasyncresult.h"
35 #include "gsocketaddress.h"
36
37
38 G_DEFINE_TYPE (GWin32Resolver, g_win32_resolver, G_TYPE_THREADED_RESOLVER)
39
40 static void
41 g_win32_resolver_init (GWin32Resolver *gwr)
42 {
43 }
44
45 /* This is simpler than GThreadedResolver since we don't have to worry
46  * about multiple application-level threads, but more complicated than
47  * GUnixResolver, since we do have to deal with multiple threads of
48  * our own.
49  *
50  * The various request possibilities:
51  *
52  * 1. Synchronous: handed off to the base class (GThreadedResolver);
53  *    since it's never possible to cancel a synchronous request in a
54  *    single-threaded program, the request is done in the calling
55  *    thread.
56  *
57  * 2. Asynchronous: A GWin32ResolverRequest is created with
58  *    appropriate query-specific information, a Windows event handle,
59  *    and a GSimpleAsyncResult. This is then handed to the
60  *    Windows-internal thread pool, which does the raw DNS query part
61  *    of the operation (being careful to not call any glib methods
62  *    that might fail when called from another thread when
63  *    g_thread_init() has not been called). The main thread sets up a
64  *    GSource to asynchronously poll the event handle. There are two
65  *    sub-possibilities:
66  *
67  *      a. The resolution completes: the threadpool function calls
68  *         SetEvent() on the event handle and then returns.
69  *
70  *      b. The resolution is cancelled: request_cancelled()
71  *         disconnects the "cancelled" signal handler, and queues an
72  *         idle handler to complete the async_result.
73  *
74  *    Since we can't free the request from the threadpool thread
75  *    (because of glib locking issues), we *always* have to have it
76  *    call SetEvent and trigger the callback to indicate that it is
77  *    done. But this means that it's possible for the request to be
78  *    cancelled (queuing an idle handler to return that result) and
79  *    then have the resolution thread complete before the idle handler
80  *    runs. So the event callback and the idle handler need to both
81  *    watch out for this, making sure we don't complete the same
82  *    result twice.
83  */
84
85 typedef struct GWin32ResolverRequest GWin32ResolverRequest;
86 typedef void (*GWin32ResolverRequestFreeFunc) (GWin32ResolverRequest *);
87
88 struct GWin32ResolverRequest {
89   GWin32ResolverRequestFreeFunc free_func;
90
91   GCancellable *cancellable;
92   GError *error;
93
94   HANDLE *event;
95   GSimpleAsyncResult *async_result;
96   gboolean complete;
97   GSource *cancelled_idle;
98
99   union {
100     struct {
101       gchar *name;
102       gint retval;
103       struct addrinfo *res;
104     } name;
105
106     struct {
107       GInetAddress *iaddr;
108       struct sockaddr_storage addr;
109       gsize addrlen;
110       gint retval;
111       gchar *namebuf;
112     } address;
113
114     struct {
115       gchar *rrname;
116       DNS_STATUS retval;
117       DNS_RECORD *results;
118     } service;
119   } u;
120
121 };
122
123 static GSource *g_win32_handle_source_add (HANDLE      handle,
124                                            GSourceFunc callback,
125                                            gpointer    user_data);
126
127 static gboolean request_completed (gpointer      user_data);
128 static void     request_cancelled (GCancellable *cancellable,
129                                    gpointer      user_data);
130
131 GWin32ResolverRequest *
132 g_win32_resolver_request_new (GResolver                     *resolver,
133                               GWin32ResolverRequestFreeFunc  free_func,
134                               GCancellable                  *cancellable,
135                               GAsyncReadyCallback            callback,
136                               gpointer                       user_data,
137                               gpointer                       tag)
138 {
139   GWin32ResolverRequest *req;
140
141   req = g_slice_new0 (GWin32ResolverRequest);
142   req->free_func = free_func;
143
144   req->async_result = g_simple_async_result_new (G_OBJECT (resolver), callback,
145                                                  user_data, tag);
146   g_simple_async_result_set_op_res_gpointer (req->async_result, req, NULL);
147
148   req->event = CreateEvent (NULL, FALSE, FALSE, NULL);
149   g_win32_handle_source_add (req->event, request_completed, req);  
150
151   if (cancellable)
152     {
153       req->cancellable = g_object_ref (cancellable);
154       g_signal_connect (cancellable, "cancelled",
155                         G_CALLBACK (request_cancelled), req);
156     }
157
158   return req;
159 }
160
161 static gboolean
162 request_completed (gpointer user_data)
163 {
164   GWin32ResolverRequest *req = user_data;
165
166   /* Clean up cancellation-related stuff first */
167   if (req->cancelled_idle)
168     {
169       g_source_destroy (req->cancelled_idle);
170       g_source_unref (req->cancelled_idle);
171       req->cancelled_idle = NULL;
172     }
173   if (req->cancellable)
174     {
175       g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
176       g_object_unref (req->cancellable);
177     }
178
179   /* Now complete the result (assuming it wasn't already completed) */
180   if (req->async_result)
181     {
182       g_simple_async_result_complete (req->async_result);
183       g_object_unref (req->async_result);
184     }
185
186   /* And free req */
187   CloseHandle (req->event);
188   req->free_func (req);
189   g_slice_free (GWin32ResolverRequest, req);
190
191   return FALSE;
192 }
193
194 static gboolean
195 request_cancelled_idle (gpointer user_data)
196 {
197   GWin32ResolverRequest *req = user_data;
198   GError *error = NULL;
199
200   g_source_unref (req->cancelled_idle);
201   req->cancelled_idle = NULL;
202
203   g_cancellable_set_error_if_cancelled (req->cancellable, &error);
204   g_simple_async_result_set_from_error (req->async_result, error);
205   g_simple_async_result_complete (req->async_result);
206
207   g_object_unref (req->async_result);
208   req->async_result = NULL;
209
210   /* request_completed will eventually be called to free req */
211
212   return FALSE;
213 }
214
215 static void
216 request_cancelled (GCancellable *cancellable,
217                    gpointer      user_data)
218 {
219   GWin32ResolverRequest *req = user_data;
220
221   if (req->cancellable)
222     {
223       g_signal_handlers_disconnect_by_func (req->cancellable, request_cancelled, req);
224       g_object_unref (req->cancellable);
225       req->cancellable = NULL;
226     }
227
228   /* We need to wait until main-loop-time to actually complete the
229    * result; we don't use _complete_in_idle() here because we need to
230    * keep track of the source so we can potentially cancel it before
231    * it runs.
232    */
233   req->cancelled_idle = g_idle_source_new ();
234   g_source_set_callback (req->cancelled_idle,
235                          (GSourceFunc)request_cancelled_idle, req, NULL);
236   g_source_attach (req->cancelled_idle, g_main_context_get_thread_default ());
237 }
238
239 static DWORD WINAPI
240 lookup_by_name_in_thread (LPVOID data)
241 {
242   GWin32ResolverRequest *req = data;
243
244   req->u.name.retval = getaddrinfo (req->u.name.name, NULL,
245                                     &_g_resolver_addrinfo_hints,
246                                     &req->u.name.res);
247   SetEvent (req->event);
248   return 0;
249 }
250
251 static void
252 free_lookup_by_name (GWin32ResolverRequest *req)
253 {
254   g_free (req->u.name.name);
255   if (req->u.name.res)
256     freeaddrinfo (req->u.name.res);
257 }
258
259 static void
260 lookup_by_name_async (GResolver           *resolver,
261                       const gchar         *hostname,
262                       GCancellable        *cancellable,
263                       GAsyncReadyCallback  callback,
264                       gpointer             user_data)
265 {
266   GWin32ResolverRequest *req;
267
268   req = g_win32_resolver_request_new (resolver, free_lookup_by_name,
269                                       cancellable, callback, user_data,
270                                       lookup_by_name_async);
271   req->u.name.name = g_strdup (hostname);
272
273   QueueUserWorkItem (lookup_by_name_in_thread, req, 0);
274 }
275
276 static GList *
277 lookup_by_name_finish (GResolver     *resolver,
278                        GAsyncResult  *result,
279                        GError       **error)
280 {
281   GSimpleAsyncResult *simple;
282   GWin32ResolverRequest *req;
283
284   g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), NULL);
285   simple = G_SIMPLE_ASYNC_RESULT (result);
286
287   req = g_simple_async_result_get_op_res_gpointer (simple);
288   return _g_resolver_addresses_from_addrinfo (req->u.name.name, req->u.name.res,
289                                               req->u.name.retval, error);
290 }
291
292
293 static DWORD WINAPI
294 lookup_by_addresses_in_thread (LPVOID data)
295 {
296   GWin32ResolverRequest *req = data;
297
298   req->u.address.retval =
299     getnameinfo ((struct sockaddr *)&req->u.address.addr,
300                  req->u.address.addrlen,
301                  req->u.address.namebuf, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
302   SetEvent (req->event);
303   return 0;
304 }
305
306 static void
307 free_lookup_by_address (GWin32ResolverRequest *req)
308 {
309   g_object_unref (req->u.address.iaddr);
310   g_free (req->u.address.namebuf);
311 }
312
313 static void
314 lookup_by_address_async (GResolver           *resolver,
315                          GInetAddress        *address,
316                          GCancellable        *cancellable,
317                          GAsyncReadyCallback  callback,
318                          gpointer             user_data)
319 {
320   GWin32ResolverRequest *req;
321
322   req = g_win32_resolver_request_new (resolver, free_lookup_by_address,
323                                       cancellable, callback, user_data,
324                                       lookup_by_address_async);
325
326   req->u.address.iaddr = g_object_ref (address);
327   _g_resolver_address_to_sockaddr (address, &req->u.address.addr,
328                                    &req->u.address.addrlen);
329   req->u.address.namebuf = g_malloc (NI_MAXHOST);
330
331   QueueUserWorkItem (lookup_by_addresses_in_thread, req, 0);
332 }
333
334 static char *
335 lookup_by_address_finish (GResolver     *resolver,
336                           GAsyncResult  *result,
337                           GError       **error)
338 {
339   GSimpleAsyncResult *simple;
340   GWin32ResolverRequest *req;
341
342   g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), NULL);
343   simple = G_SIMPLE_ASYNC_RESULT (result);
344
345   req = g_simple_async_result_get_op_res_gpointer (simple);
346   return _g_resolver_name_from_nameinfo (req->u.address.iaddr,
347                                          req->u.address.namebuf,
348                                          req->u.address.retval, error);
349 }
350
351
352 static DWORD WINAPI
353 lookup_service_in_thread (LPVOID data)
354 {
355   GWin32ResolverRequest *req = data;
356
357   req->u.service.retval =
358     DnsQuery_A (req->u.service.rrname, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
359                 NULL, &req->u.service.results, NULL);
360   SetEvent (req->event);
361   return 0;
362 }
363
364 static void
365 free_lookup_service (GWin32ResolverRequest *req)
366 {
367   g_free (req->u.service.rrname);
368   if (req->u.service.results)
369     DnsRecordListFree (req->u.service.results, DnsFreeRecordList);
370 }
371
372 static void
373 lookup_service_async (GResolver           *resolver,
374                       const char          *rrname,
375                       GCancellable        *cancellable,
376                       GAsyncReadyCallback  callback,
377                       gpointer             user_data)
378 {
379   GWin32ResolverRequest *req;
380
381   req = g_win32_resolver_request_new (resolver, free_lookup_service,
382                                       cancellable, callback, user_data,
383                                       lookup_service_async);
384   req->u.service.rrname = g_strdup (rrname);
385
386   QueueUserWorkItem (lookup_service_in_thread, req, 0);
387 }
388
389 static GList *
390 lookup_service_finish (GResolver     *resolver,
391                        GAsyncResult  *result,
392                        GError       **error)
393 {
394   GSimpleAsyncResult *simple;
395   GWin32ResolverRequest *req;
396
397   g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), NULL);
398   simple = G_SIMPLE_ASYNC_RESULT (result);
399
400   req = g_simple_async_result_get_op_res_gpointer (simple);
401   return _g_resolver_targets_from_DnsQuery (req->u.service.rrname,
402                                             req->u.service.retval,
403                                             req->u.service.results, error);
404 }
405
406
407 static void
408 g_win32_resolver_class_init (GWin32ResolverClass *win32_class)
409 {
410   GResolverClass *resolver_class = G_RESOLVER_CLASS (win32_class);
411
412   resolver_class->lookup_by_name_async     = lookup_by_name_async;
413   resolver_class->lookup_by_name_finish    = lookup_by_name_finish;
414   resolver_class->lookup_by_address_async  = lookup_by_address_async;
415   resolver_class->lookup_by_address_finish = lookup_by_address_finish;
416   resolver_class->lookup_service_async     = lookup_service_async;
417   resolver_class->lookup_service_finish    = lookup_service_finish;
418 }
419
420
421 /* Windows HANDLE GSource */
422
423 typedef struct {
424   GSource source;
425   GPollFD pollfd;
426 } GWin32HandleSource;
427
428 static gboolean
429 g_win32_handle_source_prepare (GSource *source,
430                                gint    *timeout)
431 {
432   *timeout = -1;
433   return FALSE;
434 }
435
436 static gboolean
437 g_win32_handle_source_check (GSource *source)
438 {
439   GWin32HandleSource *hsource = (GWin32HandleSource *)source;
440
441   return hsource->pollfd.revents;
442 }
443
444 static gboolean
445 g_win32_handle_source_dispatch (GSource     *source,
446                                 GSourceFunc  callback,
447                                 gpointer     user_data)
448 {
449   return (*callback) (user_data);
450 }
451
452 static void
453 g_win32_handle_source_finalize (GSource *source)
454 {
455   ;
456 }
457
458 GSourceFuncs g_win32_handle_source_funcs = {
459   g_win32_handle_source_prepare,
460   g_win32_handle_source_check,
461   g_win32_handle_source_dispatch,
462   g_win32_handle_source_finalize
463 };
464
465 static GSource *
466 g_win32_handle_source_add (HANDLE      handle,
467                            GSourceFunc callback,
468                            gpointer    user_data)
469 {
470   GWin32HandleSource *hsource;
471   GSource *source;
472
473   source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource));
474   hsource = (GWin32HandleSource *)source;
475   hsource->pollfd.fd = (gint)handle;
476   hsource->pollfd.events = G_IO_IN;
477   hsource->pollfd.revents = 0;
478   g_source_add_poll (source, &hsource->pollfd);
479
480   g_source_set_callback (source, callback, user_data, NULL);
481   g_source_attach (source, g_main_context_get_thread_default ());
482   return source;
483 }