Commit 65cc5d89 authored by Dan Winship's avatar Dan Winship

Support g_main_context_push_thread_default() in gio

GFile allows for the possibility that external implementations may not
support thread-default contexts yet, via
g_file_supports_thread_contexts(). GVolumeMonitor is not yet
thread-default-context aware.

Add a test program to verify that basic gio async ops work correctly
in non-default contexts.

http://bugzilla.gnome.org/show_bug.cgi?id=579984
parent 4363f193
......@@ -425,6 +425,8 @@ g_dummy_file_file_iface_init (GFileIface *iface)
iface->get_relative_path = g_dummy_file_get_relative_path;
iface->resolve_relative_path = g_dummy_file_resolve_relative_path;
iface->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
iface->supports_thread_contexts = TRUE;
}
/* Uri handling helper functions: */
......
......@@ -6751,5 +6751,30 @@ g_file_stop_mountable_finish (GFile *file,
return (* iface->stop_mountable_finish) (file, result, error);
}
/**
* g_file_supports_thread_contexts:
* @file: a #GFile.
*
* Checks if @file supports <link
* linkend="g-main-context-push-thread-default-context">thread-default
* contexts</link>. If this returns %FALSE, you cannot perform
* asynchronous operations on @file in a thread that has a
* thread-default context.
*
* Returns: Whether or not @file supports thread-default contexts.
*
* Since: 2.22
*/
gboolean
g_file_supports_thread_contexts (GFile *file)
{
GFileIface *iface;
g_return_val_if_fail (G_IS_FILE (file), FALSE);
iface = G_FILE_GET_IFACE (file);
return iface->supports_thread_contexts;
}
#define __G_FILE_C__
#include "gioaliasdef.c"
......@@ -505,6 +505,7 @@ struct _GFileIface
gboolean (* stop_mountable_finish) (GFile *file,
GAsyncResult *result,
GError **error);
gboolean supports_thread_contexts;
};
GType g_file_get_type (void) G_GNUC_CONST;
......@@ -940,6 +941,8 @@ gboolean g_file_replace_contents_finish (GFile *file,
char **new_etag,
GError **error);
gboolean g_file_supports_thread_contexts (GFile *file);
G_END_DECLS
#endif /* __G_FILE_H__ */
......@@ -47,8 +47,14 @@ static void file_change_free (FileChange *change);
* g_file_monitor(), g_file_monitor_file(), or
* g_file_monitor_directory().
*
* To get informed about changes to the file or directory you
* are monitoring, connect to the #GFileMonitor::changed signal.
* To get informed about changes to the file or directory you are
* monitoring, connect to the #GFileMonitor::changed signal. The
* signal will be emitted in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* context</link> of the thread that the monitor was created in
* (though if the global default main context is blocked, this may
* cause notifications to be blocked even if the thread-default
* context is still running).
**/
G_LOCK_DEFINE_STATIC(cancelled);
......@@ -82,6 +88,8 @@ struct _GFileMonitorPrivate {
GSource *timeout;
guint32 timeout_fires_at;
GMainContext *context;
};
enum {
......@@ -169,6 +177,9 @@ g_file_monitor_finalize (GObject *object)
g_hash_table_destroy (monitor->priv->rate_limiter);
if (monitor->priv->context)
g_main_context_unref (monitor->priv->context);
G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object);
}
......@@ -258,6 +269,7 @@ g_file_monitor_init (GFileMonitor *monitor)
monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
NULL, (GDestroyNotify) rate_limiter_free);
monitor->priv->context = g_main_context_get_thread_default ();
}
/**
......@@ -414,7 +426,7 @@ emit_in_idle (GFileMonitor *monitor,
* pending idles.
*/
g_source_set_callback (source, emit_cb, monitor, NULL);
g_source_attach (source, NULL);
g_source_attach (source, monitor->priv->context);
}
/* We reverse this in the processor */
priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change);
......@@ -570,7 +582,7 @@ rate_limiter_timeout (gpointer timeout_data)
{
source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
g_source_attach (source, NULL);
g_source_attach (source, monitor->priv->context);
monitor->priv->timeout = source;
monitor->priv->timeout_fires_at = data.time_now + data.min_time;
......@@ -622,7 +634,7 @@ update_rate_limiter_timeout (GFileMonitor *monitor,
{
source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
g_source_attach (source, NULL);
g_source_attach (source, monitor->priv->context);
monitor->priv->timeout = source;
monitor->priv->timeout_fires_at = data.time_now + data.min_time;
......@@ -640,7 +652,9 @@ update_rate_limiter_timeout (GFileMonitor *monitor,
* has taken place. Should be called from file monitor
* implementations only.
*
* The signal will be emitted from an idle handler.
* The signal will be emitted from an idle handler (in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* context</link>).
**/
void
g_file_monitor_emit_event (GFileMonitor *monitor,
......
......@@ -343,6 +343,7 @@ g_file_start_mountable
g_file_start_mountable_finish
g_file_stop_mountable
g_file_stop_mountable_finish
g_file_supports_thread_contexts
#endif
#endif
......
......@@ -55,6 +55,7 @@ struct _GIOSchedulerJob {
gint io_priority;
GCancellable *cancellable;
GMainContext *context;
guint idle_tag;
};
......@@ -72,6 +73,8 @@ g_io_job_free (GIOSchedulerJob *job)
{
if (job->cancellable)
g_object_unref (job->cancellable);
if (job->context)
g_main_context_unref (job->context);
g_free (job);
}
......@@ -242,6 +245,10 @@ g_io_scheduler_push_job (GIOSchedulerJobFunc job_func,
if (cancellable)
job->cancellable = g_object_ref (cancellable);
job->context = g_main_context_get_thread_default ();
if (job->context)
g_main_context_ref (job->context);
G_LOCK (active_jobs);
active_jobs = g_slist_prepend (active_jobs, job);
job->active_link = active_jobs;
......@@ -341,12 +348,12 @@ mainloop_proxy_free (MainLoopProxy *proxy)
/**
* g_io_scheduler_job_send_to_mainloop:
* @job: a #GIOSchedulerJob
* @func: a #GSourceFunc callback that will be called in the main thread
* @func: a #GSourceFunc callback that will be called in the original thread
* @user_data: data to pass to @func
* @notify: a #GDestroyNotify for @user_data, or %NULL
*
* Used from an I/O job to send a callback to be run in the
* main loop (main thread), waiting for the result (and thus
* Used from an I/O job to send a callback to be run in the thread
* that the job was started from, waiting for the result (and thus
* blocking the I/O job).
*
* Returns: The return value of @func
......@@ -359,7 +366,6 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
{
GSource *source;
MainLoopProxy *proxy;
guint id;
gboolean ret_val;
g_return_val_if_fail (job != NULL, FALSE);
......@@ -389,7 +395,7 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
g_source_set_callback (source, mainloop_proxy_func, proxy,
NULL);
id = g_source_attach (source, NULL);
g_source_attach (source, job->context);
g_source_unref (source);
g_cond_wait (proxy->ack_condition, proxy->ack_lock);
......@@ -404,14 +410,14 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
/**
* g_io_scheduler_job_send_to_mainloop_async:
* @job: a #GIOSchedulerJob
* @func: a #GSourceFunc callback that will be called in the main thread
* @func: a #GSourceFunc callback that will be called in the original thread
* @user_data: data to pass to @func
* @notify: a #GDestroyNotify for @user_data, or %NULL
*
* Used from an I/O job to send a callback to be run asynchronously
* in the main loop (main thread). The callback will be run when the
* main loop is available, but at that time the I/O job might have
* finished. The return value from the callback is ignored.
* Used from an I/O job to send a callback to be run asynchronously in
* the thread that the job was started from. The callback will be run
* when the main loop is available, but at that time the I/O job might
* have finished. The return value from the callback is ignored.
*
* Note that if you are passing the @user_data from g_io_scheduler_push_job()
* on to this function you have to ensure that it is not freed before
......@@ -426,7 +432,6 @@ g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
{
GSource *source;
MainLoopProxy *proxy;
guint id;
g_return_if_fail (job != NULL);
g_return_if_fail (func != NULL);
......@@ -452,7 +457,7 @@ g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
g_source_set_callback (source, mainloop_proxy_func, proxy,
(GDestroyNotify)mainloop_proxy_free);
id = g_source_attach (source, NULL);
g_source_attach (source, job->context);
g_source_unref (source);
}
......
......@@ -2392,4 +2392,6 @@ g_local_file_file_iface_init (GFileIface *iface)
iface->move = g_local_file_move;
iface->monitor_dir = g_local_file_monitor_dir;
iface->monitor_file = g_local_file_monitor_file;
iface->supports_thread_contexts = TRUE;
}
......@@ -89,11 +89,13 @@
*
* GSimpleAsyncResult can integrate into GLib's event loop, #GMainLoop,
* or it can use #GThread<!-- -->s if available.
* g_simple_async_result_complete() will finish an I/O task directly within
* the main event loop. g_simple_async_result_complete_in_idle() will
* integrate the I/O task into the main event loop as an idle function and
* g_simple_async_result_run_in_thread() will run the job in a separate
* thread.
* g_simple_async_result_complete() will finish an I/O task directly
* from the point where it is called. g_simple_async_result_complete_in_idle()
* will finish it from an idle handler in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* context</link>. g_simple_async_result_run_in_thread() will run the
* job in a separate thread and then deliver the result to the
* thread-default main context.
*
* To set the results of an asynchronous function,
* g_simple_async_result_set_op_res_gpointer(),
......@@ -119,6 +121,7 @@ struct _GSimpleAsyncResult
GObject *source_object;
GAsyncReadyCallback callback;
gpointer user_data;
GMainContext *context;
GError *error;
gboolean failed;
gboolean handle_cancellation;
......@@ -163,6 +166,9 @@ g_simple_async_result_finalize (GObject *object)
if (simple->source_object)
g_object_unref (simple->source_object);
if (simple->context)
g_main_context_unref (simple->context);
clear_op_res (simple);
if (simple->error)
......@@ -183,6 +189,10 @@ static void
g_simple_async_result_init (GSimpleAsyncResult *simple)
{
simple->handle_cancellation = TRUE;
simple->context = g_main_context_get_thread_default ();
if (simple->context)
g_main_context_ref (simple->context);
}
/**
......@@ -547,16 +557,35 @@ g_simple_async_result_set_error (GSimpleAsyncResult *simple,
* g_simple_async_result_complete:
* @simple: a #GSimpleAsyncResult.
*
* Completes an asynchronous I/O job.
* Must be called in the main thread, as it invokes the callback that
* should be called in the main thread. If you are in a different thread
* use g_simple_async_result_complete_in_idle().
* Completes an asynchronous I/O job immediately. Must be called in
* the thread where the asynchronous result was to be delivered, as it
* invokes the callback directly. If you are in a different thread use
* g_simple_async_result_complete_in_idle().
**/
void
g_simple_async_result_complete (GSimpleAsyncResult *simple)
{
#ifndef G_DISABLE_CHECKS
GSource *current_source;
GMainContext *current_context;
#endif
g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
#ifndef G_DISABLE_CHECKS
current_source = g_main_current_source ();
if (current_source)
{
current_context = g_source_get_context (current_source);
if (current_context == g_main_context_default ())
current_context = NULL;
if (simple->context != current_context)
g_warning ("g_simple_async_result_complete() called from wrong context!");
}
else
g_warning ("g_simple_async_result_complete() called from outside main loop!");
#endif
if (simple->callback)
simple->callback (simple->source_object,
G_ASYNC_RESULT (simple),
......@@ -577,14 +606,14 @@ complete_in_idle_cb (gpointer data)
* g_simple_async_result_complete_in_idle:
* @simple: a #GSimpleAsyncResult.
*
* Completes an asynchronous function in the main event loop using
* an idle function.
* Completes an asynchronous function in an idle handler in the <link
* linkend="g-main-context-push-thread-default">thread-default main
* loop</link> of the thread that @simple was initially created in.
**/
void
g_simple_async_result_complete_in_idle (GSimpleAsyncResult *simple)
{
GSource *source;
guint id;
g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
......@@ -594,7 +623,7 @@ g_simple_async_result_complete_in_idle (GSimpleAsyncResult *simple)
g_source_set_priority (source, G_PRIORITY_DEFAULT);
g_source_set_callback (source, complete_in_idle_cb, simple, g_object_unref);
id = g_source_attach (source, NULL);
g_source_attach (source, simple->context);
g_source_unref (source);
}
......@@ -638,7 +667,6 @@ run_in_thread (GIOSchedulerJob *job,
RunInThreadData *data = _data;
GSimpleAsyncResult *simple = data->simple;
GSource *source;
guint id;
if (simple->handle_cancellation &&
g_cancellable_is_cancelled (c))
......@@ -655,7 +683,7 @@ run_in_thread (GIOSchedulerJob *job,
g_source_set_priority (source, G_PRIORITY_DEFAULT);
g_source_set_callback (source, complete_in_idle_cb_for_thread, data, NULL);
id = g_source_attach (source, NULL);
g_source_attach (source, simple->context);
g_source_unref (source);
return FALSE;
......@@ -668,7 +696,9 @@ run_in_thread (GIOSchedulerJob *job,
* @io_priority: the io priority of the request.
* @cancellable: optional #GCancellable object, %NULL to ignore.
*
* Runs the asynchronous job in a separated thread.
* Runs the asynchronous job in a separate thread and then calls
* g_simple_async_result_complete_in_idle() on @simple to return
* the result to the appropriate main loop.
**/
void
g_simple_async_result_run_in_thread (GSimpleAsyncResult *simple,
......
......@@ -796,7 +796,7 @@ g_socket_client_enumerator_callback (GObject *object,
g_source_set_callback (source,
(GSourceFunc) g_socket_client_socket_callback,
data, NULL);
g_source_attach (source, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
g_object_unref (address);
......
......@@ -188,7 +188,7 @@ g_socket_input_stream_read_async (GInputStream *stream,
g_source_set_callback (source,
(GSourceFunc) g_socket_input_stream_read_ready,
g_object_ref (input_stream), g_object_unref);
g_source_attach (source, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
}
else
......
......@@ -765,7 +765,7 @@ g_socket_listener_accept_socket_async (GSocketListener *listener,
accept_ready,
data,
cancellable,
NULL);
g_main_context_get_thread_default ());
}
/**
......
......@@ -190,7 +190,7 @@ g_socket_output_stream_write_async (GOutputStream *stream,
g_source_set_callback (source,
(GSourceFunc) g_socket_output_stream_write_ready,
g_object_ref (output_stream), g_object_unref);
g_source_attach (source, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
}
else
......
......@@ -321,7 +321,7 @@ g_tcp_connection_close_async (GIOStream *stream,
g_source_set_callback (source,
(GSourceFunc) close_read_ready,
data, (GDestroyNotify)close_async_data_free);
g_source_attach (source, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
return;
......
......@@ -507,7 +507,7 @@ g_unix_input_stream_read_async (GInputStream *stream,
cancellable);
g_source_set_callback (source, (GSourceFunc)read_async_cb, data, g_free);
g_source_attach (source, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
}
......@@ -635,7 +635,7 @@ g_unix_input_stream_close_async (GInputStream *stream,
idle = g_idle_source_new ();
g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, close_async_data_free);
g_source_attach (idle, NULL);
g_source_attach (idle, g_main_context_get_thread_default ());
g_source_unref (idle);
}
......
......@@ -361,11 +361,11 @@ eject_unmount_do_cb (gpointer user_data)
data->error_channel_source = g_io_create_watch (data->error_channel, G_IO_IN);
g_source_set_callback (data->error_channel_source,
(GSourceFunc) eject_unmount_read_error, data, NULL);
g_source_attach (data->error_channel_source, NULL);
g_source_attach (data->error_channel_source, g_main_context_get_thread_default ());
child_watch = g_child_watch_source_new (child_pid);
g_source_set_callback (child_watch, (GSourceFunc) eject_unmount_cb, data, NULL);
g_source_attach (child_watch, NULL);
g_source_attach (child_watch, g_main_context_get_thread_default ());
g_source_unref (child_watch);
handle_error:
......
......@@ -494,7 +494,7 @@ g_unix_output_stream_write_async (GOutputStream *stream,
cancellable);
g_source_set_callback (source, (GSourceFunc)write_async_cb, data, g_free);
g_source_attach (source, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
}
......@@ -591,7 +591,7 @@ g_unix_output_stream_close_async (GOutputStream *stream,
idle = g_idle_source_new ();
g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, g_free);
g_source_attach (idle, NULL);
g_source_attach (idle, g_main_context_get_thread_default ());
g_source_unref (idle);
}
......
......@@ -84,21 +84,16 @@ g_unix_resolver_finalize (GObject *object)
* a. The resolution completes: g_unix_resolver_watch() sees that
* the request has completed, and calls
* g_unix_resolver_request_complete(), which detaches the
* "cancelled" signal handler (if it was present) and then
* immediately completes the async_result (since
* g_unix_resolver_watch() is already run from main-loop
* time.) After completing the async_result, it unrefs it,
* causing the req to be freed as well.
* "cancelled" signal handler (if it was present), queues the
* async_result to be completed, and then unrefs it.
*
* b. The resolution is cancelled: request_cancelled() calls
* _g_asyncns_cancel() to cancel the resolution. Then it calls
* g_unix_resolver_request_complete(), which detaches the
* signal handler, and queues async_result to complete in an
* idle handler. It then unrefs the async_result to ensure
* that after its callback runs, it will be destroyed, in turn
* causing the req to be freed. Because the asyncns resolution
* was cancelled, g_unix_resolver_watch() will never be
* triggered for this req.
* idle handler. Because the asyncns resolution was cancelled,
* g_unix_resolver_watch() will never be triggered for this
* req.
*
* Since there's only a single thread, it's not possible for the
* request to both complete and be cancelled "at the same time",
......@@ -108,18 +103,29 @@ g_unix_resolver_finalize (GObject *object)
*/
typedef struct _GUnixResolverRequest GUnixResolverRequest;
typedef void (*GUnixResolverFreeFunc) (GUnixResolverRequest *);
typedef void (*GUnixResolverFunc) (GUnixResolverRequest *);
struct _GUnixResolverRequest {
GUnixResolver *gur;
_g_asyncns_query_t *qy;
union {
gchar *hostname;
GInetAddress *address;
gchar *service;
struct {
gchar *hostname;
GList *addresses;
} name;
struct {
GInetAddress *address;
gchar *hostname;
} address;
struct {
gchar *service;
GList *targets;
} service;
} u;
GUnixResolverFreeFunc free_func;
GUnixResolverFunc process_func, free_func;
GCancellable *cancellable;
GSimpleAsyncResult *async_result;
......@@ -133,7 +139,8 @@ static void request_cancelled (GCancellable *cancellable,
static GUnixResolverRequest *
g_unix_resolver_request_new (GUnixResolver *gur,
_g_asyncns_query_t *qy,
GUnixResolverFreeFunc free_func,
GUnixResolverFunc process_func,
GUnixResolverFunc free_func,
GCancellable *cancellable,
GSimpleAsyncResult *async_result)
{
......@@ -142,6 +149,7 @@ g_unix_resolver_request_new (GUnixResolver *gur,
req = g_slice_new0 (GUnixResolverRequest);
req->gur = g_object_ref (gur);
req->qy = qy;
req->process_func = process_func;
req->free_func = free_func;
if (cancellable)
......@@ -161,9 +169,8 @@ g_unix_resolver_request_new (GUnixResolver *gur,
static void
g_unix_resolver_request_free (GUnixResolverRequest *req)
{
/* If the user didn't call _finish the qy will still be around. */
if (req->qy)
_g_asyncns_cancel (req->gur->asyncns, req->qy);
req->free_func (req);
g_object_unref (req->gur);
/* We don't have to free req->cancellable and req->async_result,
* since they must already have been freed if we're here.
......@@ -173,8 +180,7 @@ g_unix_resolver_request_free (GUnixResolverRequest *req)
}
static void
g_unix_resolver_request_complete (GUnixResolverRequest *req,
gboolean need_idle)
g_unix_resolver_request_complete (GUnixResolverRequest *req)
{
if (req->cancellable)
{
......@@ -183,16 +189,11 @@ g_unix_resolver_request_complete (GUnixResolverRequest *req,
req->cancellable = NULL;
}
if (need_idle)
g_simple_async_result_complete_in_idle (req->async_result);
else
g_simple_async_result_complete (req->async_result);
/* If we completed_in_idle, that will have taken an extra ref on
* req->async_result; if not, then we're already done. Either way we
* need to unref the async_result to make sure it eventually is
* destroyed, causing req to be freed.
/* We always complete_in_idle, even if we were called from
* g_unix_resolver_watch(), since we might have been started under a
* non-default g_main_context_get_thread_default().
*/
g_simple_async_result_complete_in_idle (req->async_result);
g_object_unref (req->async_result);
}
......@@ -210,7 +211,7 @@ request_cancelled (GCancellable *cancellable,
g_simple_async_result_set_from_error (req->async_result, error);
g_error_free (error);
g_unix_resolver_request_complete (req, TRUE);
g_unix_resolver_request_complete (req);
}
static gboolean
......@@ -234,7 +235,8 @@ g_unix_resolver_watch (GIOChannel *iochannel,
(qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
{
req = _g_asyncns_getuserdata (gur->asyncns, qy);
g_unix_resolver_request_complete (req, FALSE);
req->process_func (req);
g_unix_resolver_request_complete (req);
}
return TRUE;
......@@ -243,7 +245,8 @@ g_unix_resolver_watch (GIOChannel *iochannel,
static GUnixResolverRequest *
resolve_async (GUnixResolver *gur,
_g_asyncns_query_t *qy,
GUnixResolverFreeFunc free_func,
GUnixResolverFunc process_func,
GUnixResolverFunc free_func,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
......@@ -253,17 +256,41 @@ resolve_async (GUnixResolver *gur,
GUnixResolverRequest *req;
result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
req = g_unix_resolver_request_new (gur, qy, free_func, cancellable, result);
req = g_unix_resolver_request_new (gur, qy, process_func, free_func,
cancellable, result);
g_object_unref (result);
_g_asyncns_setuserdata (gur->asyncns, qy, req);
return req;
}
static void
lookup_by_name_process (GUnixResolverRequest *req)
{
struct addrinfo *res;
gint retval;
GError *error = NULL;
retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
req->u.name.addresses =
_g_resolver_addresses_from_addrinfo (req->u.name.hostname,
res, retval, &error);
if (res)
freeaddrinfo (res);
if (error)
{
g_simple_async_result_set_from_error (req->async_result, error);
g_error_free (error);
}
}
static void
lookup_by_name_free (GUnixResolverRequest *req)
{
g_free (req->u.hostname);
g_free (req->u.name.hostname);
if (req->u.name.addresses)
g_resolver_free_addresses (req->u.name.addresses);
}
static void
......@@ -279,9 +306,9 @@ lookup_by_name_async (GResolver *resolver,
qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
&_g_resolver_addrinfo_hints);
req = resolve_async (gur, qy, lookup_by_name_free, cancellable,
callback, user_data, lookup_by_name_async);
req->u.hostname = g_strdup (hostname);
req = resolve_async (gur, qy, lookup_by_name_process, lookup_by_name_free,
cancellable, callback, user_data, lookup_by_name_async);
req->u.name.hostname = g_strdup (hostname);
}
static GList *
......@@ -291,28 +318,48 @@ lookup_by_name_finish (GResolver *resolver,
{
GSimpleAsyncResult *simple;
GUnixResolverRequest *req;
struct addrinfo *res;
gint retval;
GList *addresses;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (simple, error))
return NULL;
req = g_simple_async_result_get_op_res_gpointer (simple);
retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
req->qy = NULL;
addresses = _g_resolver_addresses_from_addrinfo (req->u.hostname, res, retval, error);
if (res)
freeaddrinfo (res);
addresses = req->u.name.addresses;
req->u.name.addresses = NULL;
return addresses;
}
static void
lookup_by_address_process (GUnixResolverRequest *req)
{
gchar host[NI_MAXHOST];
gint retval;
GError *error = NULL;
retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
host, sizeof (host), NULL, 0);
req->u.address.hostname =
_g_resolver_name_from_nameinfo (req->u.address.address,
host, retval, &error);
if (error)
{
g_simple_async_result_set_from_error (req->async_result, error);
g_error_free (error);
}
}
static void
lookup_by_address_free (GUnixResolverRequest *req)
{
g_object_unref (req->u.address);
g_object_unref (req->u.address.address);
if (req->u.address.hostname)
g_free (req->u.address.hostname);
}
static void
......@@ -332,9 +379,10 @@ lookup_by_address_async (GResolver *resolver,
qy = _g_asyncns_getnameinfo (gur->asyncns,
(struct sockaddr *)&sockaddr, sockaddr_size,
NI_NAMEREQD, TRUE, FALSE);
req = resolve_async (gur, qy, lookup_by_address_free, cancellable,