mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-22 08:58:54 +02:00
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
This commit is contained in:
@@ -425,6 +425,8 @@ g_dummy_file_file_iface_init (GFileIface *iface)
|
|||||||
iface->get_relative_path = g_dummy_file_get_relative_path;
|
iface->get_relative_path = g_dummy_file_get_relative_path;
|
||||||
iface->resolve_relative_path = g_dummy_file_resolve_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->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
|
||||||
|
|
||||||
|
iface->supports_thread_contexts = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Uri handling helper functions: */
|
/* Uri handling helper functions: */
|
||||||
|
25
gio/gfile.c
25
gio/gfile.c
@@ -6751,5 +6751,30 @@ g_file_stop_mountable_finish (GFile *file,
|
|||||||
return (* iface->stop_mountable_finish) (file, result, error);
|
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__
|
#define __G_FILE_C__
|
||||||
#include "gioaliasdef.c"
|
#include "gioaliasdef.c"
|
||||||
|
@@ -505,6 +505,7 @@ struct _GFileIface
|
|||||||
gboolean (* stop_mountable_finish) (GFile *file,
|
gboolean (* stop_mountable_finish) (GFile *file,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
gboolean supports_thread_contexts;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType g_file_get_type (void) G_GNUC_CONST;
|
GType g_file_get_type (void) G_GNUC_CONST;
|
||||||
@@ -940,6 +941,8 @@ gboolean g_file_replace_contents_finish (GFile *file,
|
|||||||
char **new_etag,
|
char **new_etag,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
gboolean g_file_supports_thread_contexts (GFile *file);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_FILE_H__ */
|
#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(), g_file_monitor_file(), or
|
||||||
* g_file_monitor_directory().
|
* g_file_monitor_directory().
|
||||||
*
|
*
|
||||||
* To get informed about changes to the file or directory you
|
* To get informed about changes to the file or directory you are
|
||||||
* are monitoring, connect to the #GFileMonitor::changed signal.
|
* 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);
|
G_LOCK_DEFINE_STATIC(cancelled);
|
||||||
@@ -82,6 +88,8 @@ struct _GFileMonitorPrivate {
|
|||||||
|
|
||||||
GSource *timeout;
|
GSource *timeout;
|
||||||
guint32 timeout_fires_at;
|
guint32 timeout_fires_at;
|
||||||
|
|
||||||
|
GMainContext *context;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -169,6 +177,9 @@ g_file_monitor_finalize (GObject *object)
|
|||||||
|
|
||||||
g_hash_table_destroy (monitor->priv->rate_limiter);
|
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);
|
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_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
|
||||||
monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
|
monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
|
||||||
NULL, (GDestroyNotify) rate_limiter_free);
|
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.
|
* pending idles.
|
||||||
*/
|
*/
|
||||||
g_source_set_callback (source, emit_cb, monitor, NULL);
|
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 */
|
/* We reverse this in the processor */
|
||||||
priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change);
|
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 */
|
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_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 = source;
|
||||||
monitor->priv->timeout_fires_at = data.time_now + data.min_time;
|
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 */
|
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_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 = source;
|
||||||
monitor->priv->timeout_fires_at = data.time_now + data.min_time;
|
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
|
* has taken place. Should be called from file monitor
|
||||||
* implementations only.
|
* 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
|
void
|
||||||
g_file_monitor_emit_event (GFileMonitor *monitor,
|
g_file_monitor_emit_event (GFileMonitor *monitor,
|
||||||
|
@@ -343,6 +343,7 @@ g_file_start_mountable
|
|||||||
g_file_start_mountable_finish
|
g_file_start_mountable_finish
|
||||||
g_file_stop_mountable
|
g_file_stop_mountable
|
||||||
g_file_stop_mountable_finish
|
g_file_stop_mountable_finish
|
||||||
|
g_file_supports_thread_contexts
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -55,6 +55,7 @@ struct _GIOSchedulerJob {
|
|||||||
|
|
||||||
gint io_priority;
|
gint io_priority;
|
||||||
GCancellable *cancellable;
|
GCancellable *cancellable;
|
||||||
|
GMainContext *context;
|
||||||
|
|
||||||
guint idle_tag;
|
guint idle_tag;
|
||||||
};
|
};
|
||||||
@@ -72,6 +73,8 @@ g_io_job_free (GIOSchedulerJob *job)
|
|||||||
{
|
{
|
||||||
if (job->cancellable)
|
if (job->cancellable)
|
||||||
g_object_unref (job->cancellable);
|
g_object_unref (job->cancellable);
|
||||||
|
if (job->context)
|
||||||
|
g_main_context_unref (job->context);
|
||||||
g_free (job);
|
g_free (job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +245,10 @@ g_io_scheduler_push_job (GIOSchedulerJobFunc job_func,
|
|||||||
if (cancellable)
|
if (cancellable)
|
||||||
job->cancellable = g_object_ref (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);
|
G_LOCK (active_jobs);
|
||||||
active_jobs = g_slist_prepend (active_jobs, job);
|
active_jobs = g_slist_prepend (active_jobs, job);
|
||||||
job->active_link = active_jobs;
|
job->active_link = active_jobs;
|
||||||
@@ -341,12 +348,12 @@ mainloop_proxy_free (MainLoopProxy *proxy)
|
|||||||
/**
|
/**
|
||||||
* g_io_scheduler_job_send_to_mainloop:
|
* g_io_scheduler_job_send_to_mainloop:
|
||||||
* @job: a #GIOSchedulerJob
|
* @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
|
* @user_data: data to pass to @func
|
||||||
* @notify: a #GDestroyNotify for @user_data, or %NULL
|
* @notify: a #GDestroyNotify for @user_data, or %NULL
|
||||||
*
|
*
|
||||||
* Used from an I/O job to send a callback to be run in the
|
* Used from an I/O job to send a callback to be run in the thread
|
||||||
* main loop (main thread), waiting for the result (and thus
|
* that the job was started from, waiting for the result (and thus
|
||||||
* blocking the I/O job).
|
* blocking the I/O job).
|
||||||
*
|
*
|
||||||
* Returns: The return value of @func
|
* Returns: The return value of @func
|
||||||
@@ -359,7 +366,6 @@ g_io_scheduler_job_send_to_mainloop (GIOSchedulerJob *job,
|
|||||||
{
|
{
|
||||||
GSource *source;
|
GSource *source;
|
||||||
MainLoopProxy *proxy;
|
MainLoopProxy *proxy;
|
||||||
guint id;
|
|
||||||
gboolean ret_val;
|
gboolean ret_val;
|
||||||
|
|
||||||
g_return_val_if_fail (job != NULL, FALSE);
|
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,
|
g_source_set_callback (source, mainloop_proxy_func, proxy,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
id = g_source_attach (source, NULL);
|
g_source_attach (source, job->context);
|
||||||
g_source_unref (source);
|
g_source_unref (source);
|
||||||
|
|
||||||
g_cond_wait (proxy->ack_condition, proxy->ack_lock);
|
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:
|
* g_io_scheduler_job_send_to_mainloop_async:
|
||||||
* @job: a #GIOSchedulerJob
|
* @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
|
* @user_data: data to pass to @func
|
||||||
* @notify: a #GDestroyNotify for @user_data, or %NULL
|
* @notify: a #GDestroyNotify for @user_data, or %NULL
|
||||||
*
|
*
|
||||||
* Used from an I/O job to send a callback to be run asynchronously
|
* Used from an I/O job to send a callback to be run asynchronously in
|
||||||
* in the main loop (main thread). The callback will be run when the
|
* the thread that the job was started from. The callback will be run
|
||||||
* main loop is available, but at that time the I/O job might have
|
* when the main loop is available, but at that time the I/O job might
|
||||||
* finished. The return value from the callback is ignored.
|
* 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()
|
* 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
|
* 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;
|
GSource *source;
|
||||||
MainLoopProxy *proxy;
|
MainLoopProxy *proxy;
|
||||||
guint id;
|
|
||||||
|
|
||||||
g_return_if_fail (job != NULL);
|
g_return_if_fail (job != NULL);
|
||||||
g_return_if_fail (func != 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,
|
g_source_set_callback (source, mainloop_proxy_func, proxy,
|
||||||
(GDestroyNotify)mainloop_proxy_free);
|
(GDestroyNotify)mainloop_proxy_free);
|
||||||
|
|
||||||
id = g_source_attach (source, NULL);
|
g_source_attach (source, job->context);
|
||||||
g_source_unref (source);
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2392,4 +2392,6 @@ g_local_file_file_iface_init (GFileIface *iface)
|
|||||||
iface->move = g_local_file_move;
|
iface->move = g_local_file_move;
|
||||||
iface->monitor_dir = g_local_file_monitor_dir;
|
iface->monitor_dir = g_local_file_monitor_dir;
|
||||||
iface->monitor_file = g_local_file_monitor_file;
|
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,
|
* GSimpleAsyncResult can integrate into GLib's event loop, #GMainLoop,
|
||||||
* or it can use #GThread<!-- -->s if available.
|
* or it can use #GThread<!-- -->s if available.
|
||||||
* g_simple_async_result_complete() will finish an I/O task directly within
|
* g_simple_async_result_complete() will finish an I/O task directly
|
||||||
* the main event loop. g_simple_async_result_complete_in_idle() will
|
* from the point where it is called. g_simple_async_result_complete_in_idle()
|
||||||
* integrate the I/O task into the main event loop as an idle function and
|
* will finish it from an idle handler in the <link
|
||||||
* g_simple_async_result_run_in_thread() will run the job in a separate
|
* linkend="g-main-context-push-thread-default">thread-default main
|
||||||
* thread.
|
* 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,
|
* To set the results of an asynchronous function,
|
||||||
* g_simple_async_result_set_op_res_gpointer(),
|
* g_simple_async_result_set_op_res_gpointer(),
|
||||||
@@ -119,6 +121,7 @@ struct _GSimpleAsyncResult
|
|||||||
GObject *source_object;
|
GObject *source_object;
|
||||||
GAsyncReadyCallback callback;
|
GAsyncReadyCallback callback;
|
||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
|
GMainContext *context;
|
||||||
GError *error;
|
GError *error;
|
||||||
gboolean failed;
|
gboolean failed;
|
||||||
gboolean handle_cancellation;
|
gboolean handle_cancellation;
|
||||||
@@ -163,6 +166,9 @@ g_simple_async_result_finalize (GObject *object)
|
|||||||
if (simple->source_object)
|
if (simple->source_object)
|
||||||
g_object_unref (simple->source_object);
|
g_object_unref (simple->source_object);
|
||||||
|
|
||||||
|
if (simple->context)
|
||||||
|
g_main_context_unref (simple->context);
|
||||||
|
|
||||||
clear_op_res (simple);
|
clear_op_res (simple);
|
||||||
|
|
||||||
if (simple->error)
|
if (simple->error)
|
||||||
@@ -183,6 +189,10 @@ static void
|
|||||||
g_simple_async_result_init (GSimpleAsyncResult *simple)
|
g_simple_async_result_init (GSimpleAsyncResult *simple)
|
||||||
{
|
{
|
||||||
simple->handle_cancellation = TRUE;
|
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:
|
* g_simple_async_result_complete:
|
||||||
* @simple: a #GSimpleAsyncResult.
|
* @simple: a #GSimpleAsyncResult.
|
||||||
*
|
*
|
||||||
* Completes an asynchronous I/O job.
|
* Completes an asynchronous I/O job immediately. Must be called in
|
||||||
* Must be called in the main thread, as it invokes the callback that
|
* the thread where the asynchronous result was to be delivered, as it
|
||||||
* should be called in the main thread. If you are in a different thread
|
* invokes the callback directly. If you are in a different thread use
|
||||||
* use g_simple_async_result_complete_in_idle().
|
* g_simple_async_result_complete_in_idle().
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
g_simple_async_result_complete (GSimpleAsyncResult *simple)
|
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));
|
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)
|
if (simple->callback)
|
||||||
simple->callback (simple->source_object,
|
simple->callback (simple->source_object,
|
||||||
G_ASYNC_RESULT (simple),
|
G_ASYNC_RESULT (simple),
|
||||||
@@ -577,14 +606,14 @@ complete_in_idle_cb (gpointer data)
|
|||||||
* g_simple_async_result_complete_in_idle:
|
* g_simple_async_result_complete_in_idle:
|
||||||
* @simple: a #GSimpleAsyncResult.
|
* @simple: a #GSimpleAsyncResult.
|
||||||
*
|
*
|
||||||
* Completes an asynchronous function in the main event loop using
|
* Completes an asynchronous function in an idle handler in the <link
|
||||||
* an idle function.
|
* linkend="g-main-context-push-thread-default">thread-default main
|
||||||
|
* loop</link> of the thread that @simple was initially created in.
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
g_simple_async_result_complete_in_idle (GSimpleAsyncResult *simple)
|
g_simple_async_result_complete_in_idle (GSimpleAsyncResult *simple)
|
||||||
{
|
{
|
||||||
GSource *source;
|
GSource *source;
|
||||||
guint id;
|
|
||||||
|
|
||||||
g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
|
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_priority (source, G_PRIORITY_DEFAULT);
|
||||||
g_source_set_callback (source, complete_in_idle_cb, simple, g_object_unref);
|
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);
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +667,6 @@ run_in_thread (GIOSchedulerJob *job,
|
|||||||
RunInThreadData *data = _data;
|
RunInThreadData *data = _data;
|
||||||
GSimpleAsyncResult *simple = data->simple;
|
GSimpleAsyncResult *simple = data->simple;
|
||||||
GSource *source;
|
GSource *source;
|
||||||
guint id;
|
|
||||||
|
|
||||||
if (simple->handle_cancellation &&
|
if (simple->handle_cancellation &&
|
||||||
g_cancellable_is_cancelled (c))
|
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_priority (source, G_PRIORITY_DEFAULT);
|
||||||
g_source_set_callback (source, complete_in_idle_cb_for_thread, data, NULL);
|
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);
|
g_source_unref (source);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -668,7 +696,9 @@ run_in_thread (GIOSchedulerJob *job,
|
|||||||
* @io_priority: the io priority of the request.
|
* @io_priority: the io priority of the request.
|
||||||
* @cancellable: optional #GCancellable object, %NULL to ignore.
|
* @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
|
void
|
||||||
g_simple_async_result_run_in_thread (GSimpleAsyncResult *simple,
|
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,
|
g_source_set_callback (source,
|
||||||
(GSourceFunc) g_socket_client_socket_callback,
|
(GSourceFunc) g_socket_client_socket_callback,
|
||||||
data, NULL);
|
data, NULL);
|
||||||
g_source_attach (source, NULL);
|
g_source_attach (source, g_main_context_get_thread_default ());
|
||||||
g_source_unref (source);
|
g_source_unref (source);
|
||||||
|
|
||||||
g_object_unref (address);
|
g_object_unref (address);
|
||||||
|
@@ -188,7 +188,7 @@ g_socket_input_stream_read_async (GInputStream *stream,
|
|||||||
g_source_set_callback (source,
|
g_source_set_callback (source,
|
||||||
(GSourceFunc) g_socket_input_stream_read_ready,
|
(GSourceFunc) g_socket_input_stream_read_ready,
|
||||||
g_object_ref (input_stream), g_object_unref);
|
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);
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -765,7 +765,7 @@ g_socket_listener_accept_socket_async (GSocketListener *listener,
|
|||||||
accept_ready,
|
accept_ready,
|
||||||
data,
|
data,
|
||||||
cancellable,
|
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,
|
g_source_set_callback (source,
|
||||||
(GSourceFunc) g_socket_output_stream_write_ready,
|
(GSourceFunc) g_socket_output_stream_write_ready,
|
||||||
g_object_ref (output_stream), g_object_unref);
|
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);
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -321,7 +321,7 @@ g_tcp_connection_close_async (GIOStream *stream,
|
|||||||
g_source_set_callback (source,
|
g_source_set_callback (source,
|
||||||
(GSourceFunc) close_read_ready,
|
(GSourceFunc) close_read_ready,
|
||||||
data, (GDestroyNotify)close_async_data_free);
|
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);
|
g_source_unref (source);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@@ -507,7 +507,7 @@ g_unix_input_stream_read_async (GInputStream *stream,
|
|||||||
cancellable);
|
cancellable);
|
||||||
|
|
||||||
g_source_set_callback (source, (GSourceFunc)read_async_cb, data, g_free);
|
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);
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
@@ -635,7 +635,7 @@ g_unix_input_stream_close_async (GInputStream *stream,
|
|||||||
|
|
||||||
idle = g_idle_source_new ();
|
idle = g_idle_source_new ();
|
||||||
g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, close_async_data_free);
|
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);
|
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);
|
data->error_channel_source = g_io_create_watch (data->error_channel, G_IO_IN);
|
||||||
g_source_set_callback (data->error_channel_source,
|
g_source_set_callback (data->error_channel_source,
|
||||||
(GSourceFunc) eject_unmount_read_error, data, NULL);
|
(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);
|
child_watch = g_child_watch_source_new (child_pid);
|
||||||
g_source_set_callback (child_watch, (GSourceFunc) eject_unmount_cb, data, NULL);
|
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);
|
g_source_unref (child_watch);
|
||||||
|
|
||||||
handle_error:
|
handle_error:
|
||||||
|
@@ -494,7 +494,7 @@ g_unix_output_stream_write_async (GOutputStream *stream,
|
|||||||
cancellable);
|
cancellable);
|
||||||
|
|
||||||
g_source_set_callback (source, (GSourceFunc)write_async_cb, data, g_free);
|
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);
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
@@ -591,7 +591,7 @@ g_unix_output_stream_close_async (GOutputStream *stream,
|
|||||||
|
|
||||||
idle = g_idle_source_new ();
|
idle = g_idle_source_new ();
|
||||||
g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, g_free);
|
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);
|
g_source_unref (idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -84,21 +84,16 @@ g_unix_resolver_finalize (GObject *object)
|
|||||||
* a. The resolution completes: g_unix_resolver_watch() sees that
|
* a. The resolution completes: g_unix_resolver_watch() sees that
|
||||||
* the request has completed, and calls
|
* the request has completed, and calls
|
||||||
* g_unix_resolver_request_complete(), which detaches the
|
* g_unix_resolver_request_complete(), which detaches the
|
||||||
* "cancelled" signal handler (if it was present) and then
|
* "cancelled" signal handler (if it was present), queues the
|
||||||
* immediately completes the async_result (since
|
* async_result to be completed, and then unrefs it.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* b. The resolution is cancelled: request_cancelled() calls
|
* b. The resolution is cancelled: request_cancelled() calls
|
||||||
* _g_asyncns_cancel() to cancel the resolution. Then it calls
|
* _g_asyncns_cancel() to cancel the resolution. Then it calls
|
||||||
* g_unix_resolver_request_complete(), which detaches the
|
* g_unix_resolver_request_complete(), which detaches the
|
||||||
* signal handler, and queues async_result to complete in an
|
* signal handler, and queues async_result to complete in an
|
||||||
* idle handler. It then unrefs the async_result to ensure
|
* idle handler. Because the asyncns resolution was cancelled,
|
||||||
* that after its callback runs, it will be destroyed, in turn
|
* g_unix_resolver_watch() will never be triggered for this
|
||||||
* causing the req to be freed. Because the asyncns resolution
|
* req.
|
||||||
* 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
|
* Since there's only a single thread, it's not possible for the
|
||||||
* request to both complete and be cancelled "at the same time",
|
* 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 struct _GUnixResolverRequest GUnixResolverRequest;
|
||||||
typedef void (*GUnixResolverFreeFunc) (GUnixResolverRequest *);
|
typedef void (*GUnixResolverFunc) (GUnixResolverRequest *);
|
||||||
|
|
||||||
struct _GUnixResolverRequest {
|
struct _GUnixResolverRequest {
|
||||||
GUnixResolver *gur;
|
GUnixResolver *gur;
|
||||||
|
|
||||||
_g_asyncns_query_t *qy;
|
_g_asyncns_query_t *qy;
|
||||||
union {
|
union {
|
||||||
gchar *hostname;
|
struct {
|
||||||
GInetAddress *address;
|
gchar *hostname;
|
||||||
gchar *service;
|
GList *addresses;
|
||||||
|
} name;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
GInetAddress *address;
|
||||||
|
gchar *hostname;
|
||||||
|
} address;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
gchar *service;
|
||||||
|
GList *targets;
|
||||||
|
} service;
|
||||||
} u;
|
} u;
|
||||||
GUnixResolverFreeFunc free_func;
|
GUnixResolverFunc process_func, free_func;
|
||||||
|
|
||||||
GCancellable *cancellable;
|
GCancellable *cancellable;
|
||||||
GSimpleAsyncResult *async_result;
|
GSimpleAsyncResult *async_result;
|
||||||
@@ -133,7 +139,8 @@ static void request_cancelled (GCancellable *cancellable,
|
|||||||
static GUnixResolverRequest *
|
static GUnixResolverRequest *
|
||||||
g_unix_resolver_request_new (GUnixResolver *gur,
|
g_unix_resolver_request_new (GUnixResolver *gur,
|
||||||
_g_asyncns_query_t *qy,
|
_g_asyncns_query_t *qy,
|
||||||
GUnixResolverFreeFunc free_func,
|
GUnixResolverFunc process_func,
|
||||||
|
GUnixResolverFunc free_func,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GSimpleAsyncResult *async_result)
|
GSimpleAsyncResult *async_result)
|
||||||
{
|
{
|
||||||
@@ -142,6 +149,7 @@ g_unix_resolver_request_new (GUnixResolver *gur,
|
|||||||
req = g_slice_new0 (GUnixResolverRequest);
|
req = g_slice_new0 (GUnixResolverRequest);
|
||||||
req->gur = g_object_ref (gur);
|
req->gur = g_object_ref (gur);
|
||||||
req->qy = qy;
|
req->qy = qy;
|
||||||
|
req->process_func = process_func;
|
||||||
req->free_func = free_func;
|
req->free_func = free_func;
|
||||||
|
|
||||||
if (cancellable)
|
if (cancellable)
|
||||||
@@ -161,9 +169,8 @@ g_unix_resolver_request_new (GUnixResolver *gur,
|
|||||||
static void
|
static void
|
||||||
g_unix_resolver_request_free (GUnixResolverRequest *req)
|
g_unix_resolver_request_free (GUnixResolverRequest *req)
|
||||||
{
|
{
|
||||||
/* If the user didn't call _finish the qy will still be around. */
|
req->free_func (req);
|
||||||
if (req->qy)
|
g_object_unref (req->gur);
|
||||||
_g_asyncns_cancel (req->gur->asyncns, req->qy);
|
|
||||||
|
|
||||||
/* We don't have to free req->cancellable and req->async_result,
|
/* We don't have to free req->cancellable and req->async_result,
|
||||||
* since they must already have been freed if we're here.
|
* since they must already have been freed if we're here.
|
||||||
@@ -173,8 +180,7 @@ g_unix_resolver_request_free (GUnixResolverRequest *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
g_unix_resolver_request_complete (GUnixResolverRequest *req,
|
g_unix_resolver_request_complete (GUnixResolverRequest *req)
|
||||||
gboolean need_idle)
|
|
||||||
{
|
{
|
||||||
if (req->cancellable)
|
if (req->cancellable)
|
||||||
{
|
{
|
||||||
@@ -183,16 +189,11 @@ g_unix_resolver_request_complete (GUnixResolverRequest *req,
|
|||||||
req->cancellable = NULL;
|
req->cancellable = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (need_idle)
|
/* We always complete_in_idle, even if we were called from
|
||||||
g_simple_async_result_complete_in_idle (req->async_result);
|
* g_unix_resolver_watch(), since we might have been started under a
|
||||||
else
|
* non-default g_main_context_get_thread_default().
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
|
g_simple_async_result_complete_in_idle (req->async_result);
|
||||||
g_object_unref (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_simple_async_result_set_from_error (req->async_result, error);
|
||||||
g_error_free (error);
|
g_error_free (error);
|
||||||
|
|
||||||
g_unix_resolver_request_complete (req, TRUE);
|
g_unix_resolver_request_complete (req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -234,7 +235,8 @@ g_unix_resolver_watch (GIOChannel *iochannel,
|
|||||||
(qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
|
(qy = _g_asyncns_getnext (gur->asyncns)) != NULL)
|
||||||
{
|
{
|
||||||
req = _g_asyncns_getuserdata (gur->asyncns, qy);
|
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;
|
return TRUE;
|
||||||
@@ -243,7 +245,8 @@ g_unix_resolver_watch (GIOChannel *iochannel,
|
|||||||
static GUnixResolverRequest *
|
static GUnixResolverRequest *
|
||||||
resolve_async (GUnixResolver *gur,
|
resolve_async (GUnixResolver *gur,
|
||||||
_g_asyncns_query_t *qy,
|
_g_asyncns_query_t *qy,
|
||||||
GUnixResolverFreeFunc free_func,
|
GUnixResolverFunc process_func,
|
||||||
|
GUnixResolverFunc free_func,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
@@ -253,17 +256,41 @@ resolve_async (GUnixResolver *gur,
|
|||||||
GUnixResolverRequest *req;
|
GUnixResolverRequest *req;
|
||||||
|
|
||||||
result = g_simple_async_result_new (G_OBJECT (gur), callback, user_data, tag);
|
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_object_unref (result);
|
||||||
_g_asyncns_setuserdata (gur->asyncns, qy, req);
|
_g_asyncns_setuserdata (gur->asyncns, qy, req);
|
||||||
|
|
||||||
return 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
|
static void
|
||||||
lookup_by_name_free (GUnixResolverRequest *req)
|
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
|
static void
|
||||||
@@ -279,9 +306,9 @@ lookup_by_name_async (GResolver *resolver,
|
|||||||
|
|
||||||
qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
|
qy = _g_asyncns_getaddrinfo (gur->asyncns, hostname, NULL,
|
||||||
&_g_resolver_addrinfo_hints);
|
&_g_resolver_addrinfo_hints);
|
||||||
req = resolve_async (gur, qy, lookup_by_name_free, cancellable,
|
req = resolve_async (gur, qy, lookup_by_name_process, lookup_by_name_free,
|
||||||
callback, user_data, lookup_by_name_async);
|
cancellable, callback, user_data, lookup_by_name_async);
|
||||||
req->u.hostname = g_strdup (hostname);
|
req->u.name.hostname = g_strdup (hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *
|
static GList *
|
||||||
@@ -291,28 +318,48 @@ lookup_by_name_finish (GResolver *resolver,
|
|||||||
{
|
{
|
||||||
GSimpleAsyncResult *simple;
|
GSimpleAsyncResult *simple;
|
||||||
GUnixResolverRequest *req;
|
GUnixResolverRequest *req;
|
||||||
struct addrinfo *res;
|
|
||||||
gint retval;
|
|
||||||
GList *addresses;
|
GList *addresses;
|
||||||
|
|
||||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_name_async), FALSE);
|
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);
|
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);
|
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||||
retval = _g_asyncns_getaddrinfo_done (req->gur->asyncns, req->qy, &res);
|
addresses = req->u.name.addresses;
|
||||||
req->qy = NULL;
|
req->u.name.addresses = NULL;
|
||||||
addresses = _g_resolver_addresses_from_addrinfo (req->u.hostname, res, retval, error);
|
|
||||||
if (res)
|
|
||||||
freeaddrinfo (res);
|
|
||||||
|
|
||||||
return addresses;
|
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
|
static void
|
||||||
lookup_by_address_free (GUnixResolverRequest *req)
|
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
|
static void
|
||||||
@@ -332,9 +379,10 @@ lookup_by_address_async (GResolver *resolver,
|
|||||||
qy = _g_asyncns_getnameinfo (gur->asyncns,
|
qy = _g_asyncns_getnameinfo (gur->asyncns,
|
||||||
(struct sockaddr *)&sockaddr, sockaddr_size,
|
(struct sockaddr *)&sockaddr, sockaddr_size,
|
||||||
NI_NAMEREQD, TRUE, FALSE);
|
NI_NAMEREQD, TRUE, FALSE);
|
||||||
req = resolve_async (gur, qy, lookup_by_address_free, cancellable,
|
req = resolve_async (gur, qy, lookup_by_address_process,
|
||||||
|
lookup_by_address_free, cancellable,
|
||||||
callback, user_data, lookup_by_address_async);
|
callback, user_data, lookup_by_address_async);
|
||||||
req->u.address = g_object_ref (address);
|
req->u.address.address = g_object_ref (address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gchar *
|
static gchar *
|
||||||
@@ -344,26 +392,53 @@ lookup_by_address_finish (GResolver *resolver,
|
|||||||
{
|
{
|
||||||
GSimpleAsyncResult *simple;
|
GSimpleAsyncResult *simple;
|
||||||
GUnixResolverRequest *req;
|
GUnixResolverRequest *req;
|
||||||
gchar host[NI_MAXHOST], *name;
|
gchar *name;
|
||||||
gint retval;
|
|
||||||
|
|
||||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), FALSE);
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_by_address_async), FALSE);
|
||||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
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);
|
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||||
retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
|
name = req->u.address.hostname;
|
||||||
host, sizeof (host), NULL, 0);
|
req->u.address.hostname = NULL;
|
||||||
req->qy = NULL;
|
|
||||||
name = _g_resolver_name_from_nameinfo (req->u.address, host, retval, error);
|
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
lookup_service_process (GUnixResolverRequest *req)
|
||||||
|
{
|
||||||
|
guchar *answer;
|
||||||
|
gint len, herr;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
|
||||||
|
if (len < 0)
|
||||||
|
herr = h_errno;
|
||||||
|
else
|
||||||
|
herr = 0;
|
||||||
|
|
||||||
|
req->u.service.targets =
|
||||||
|
_g_resolver_targets_from_res_query (req->u.service.service,
|
||||||
|
answer, len, herr, &error);
|
||||||
|
_g_asyncns_freeanswer (answer);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_simple_async_result_set_from_error (req->async_result, error);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lookup_service_free (GUnixResolverRequest *req)
|
lookup_service_free (GUnixResolverRequest *req)
|
||||||
{
|
{
|
||||||
g_free (req->u.service);
|
g_free (req->u.service.service);
|
||||||
|
if (req->u.service.targets)
|
||||||
|
g_resolver_free_targets (req->u.service.targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -378,9 +453,9 @@ lookup_service_async (GResolver *resolver,
|
|||||||
_g_asyncns_query_t *qy;
|
_g_asyncns_query_t *qy;
|
||||||
|
|
||||||
qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
|
qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
|
||||||
req = resolve_async (gur, qy, lookup_service_free, cancellable,
|
req = resolve_async (gur, qy, lookup_service_process, lookup_service_free,
|
||||||
callback, user_data, lookup_service_async);
|
cancellable, callback, user_data, lookup_service_async);
|
||||||
req->u.service = g_strdup (rrname);
|
req->u.service.service = g_strdup (rrname);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *
|
static GList *
|
||||||
@@ -390,23 +465,17 @@ lookup_service_finish (GResolver *resolver,
|
|||||||
{
|
{
|
||||||
GSimpleAsyncResult *simple;
|
GSimpleAsyncResult *simple;
|
||||||
GUnixResolverRequest *req;
|
GUnixResolverRequest *req;
|
||||||
guchar *answer;
|
|
||||||
gint len, herr;
|
|
||||||
GList *targets;
|
GList *targets;
|
||||||
|
|
||||||
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), FALSE);
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (resolver), lookup_service_async), FALSE);
|
||||||
simple = G_SIMPLE_ASYNC_RESULT (result);
|
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||||
|
|
||||||
req = g_simple_async_result_get_op_res_gpointer (simple);
|
if (g_simple_async_result_propagate_error (simple, error))
|
||||||
len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
|
return NULL;
|
||||||
req->qy = NULL;
|
|
||||||
if (len < 0)
|
|
||||||
herr = h_errno;
|
|
||||||
else
|
|
||||||
herr = 0;
|
|
||||||
|
|
||||||
targets = _g_resolver_targets_from_res_query (req->u.service, answer, len, herr, error);
|
req = g_simple_async_result_get_op_res_gpointer (simple);
|
||||||
_g_asyncns_freeanswer (answer);
|
targets = req->u.service.targets;
|
||||||
|
req->u.service.targets = NULL;
|
||||||
|
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
@@ -420,11 +420,11 @@ eject_mount_do (GVolume *volume,
|
|||||||
data->error_channel_source = g_io_create_watch (data->error_channel, G_IO_IN);
|
data->error_channel_source = g_io_create_watch (data->error_channel, G_IO_IN);
|
||||||
g_source_set_callback (data->error_channel_source,
|
g_source_set_callback (data->error_channel_source,
|
||||||
(GSourceFunc) eject_mount_read_error, data, NULL);
|
(GSourceFunc) eject_mount_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);
|
child_watch = g_child_watch_source_new (child_pid);
|
||||||
g_source_set_callback (child_watch, (GSourceFunc) eject_mount_cb, data, NULL);
|
g_source_set_callback (child_watch, (GSourceFunc) eject_mount_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);
|
g_source_unref (child_watch);
|
||||||
|
|
||||||
handle_error:
|
handle_error:
|
||||||
|
@@ -41,7 +41,12 @@
|
|||||||
* #GVolumeMonitor is for listing the user interesting devices and volumes
|
* #GVolumeMonitor is for listing the user interesting devices and volumes
|
||||||
* on the computer. In other words, what a file selector or file manager
|
* on the computer. In other words, what a file selector or file manager
|
||||||
* would show in a sidebar.
|
* would show in a sidebar.
|
||||||
**/
|
*
|
||||||
|
* #GVolumeMonitor is not <link
|
||||||
|
* linkend="g-main-context-push-thread-default">thread-default-context
|
||||||
|
* aware</link>, and so should not be used other than from the main
|
||||||
|
* thread, with no thread-default-context active.
|
||||||
|
**/
|
||||||
|
|
||||||
G_DEFINE_TYPE (GVolumeMonitor, g_volume_monitor, G_TYPE_OBJECT);
|
G_DEFINE_TYPE (GVolumeMonitor, g_volume_monitor, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
@@ -234,7 +234,7 @@ request_cancelled (GCancellable *cancellable,
|
|||||||
req->cancelled_idle = g_idle_source_new ();
|
req->cancelled_idle = g_idle_source_new ();
|
||||||
g_source_set_callback (req->cancelled_idle,
|
g_source_set_callback (req->cancelled_idle,
|
||||||
(GSourceFunc)request_cancelled_idle, req, NULL);
|
(GSourceFunc)request_cancelled_idle, req, NULL);
|
||||||
g_source_attach (req->cancelled_idle, NULL);
|
g_source_attach (req->cancelled_idle, g_main_context_get_thread_default ());
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD WINAPI
|
static DWORD WINAPI
|
||||||
@@ -479,7 +479,7 @@ g_win32_handle_source_add (HANDLE handle,
|
|||||||
g_source_add_poll (source, &hsource->pollfd);
|
g_source_add_poll (source, &hsource->pollfd);
|
||||||
|
|
||||||
g_source_set_callback (source, callback, user_data, NULL);
|
g_source_set_callback (source, callback, user_data, NULL);
|
||||||
g_source_attach (source, NULL);
|
g_source_attach (source, g_main_context_get_thread_default ());
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
gio/tests/.gitignore
vendored
1
gio/tests/.gitignore
vendored
@@ -21,3 +21,4 @@ send-data
|
|||||||
socket-client
|
socket-client
|
||||||
socket-server
|
socket-server
|
||||||
srvtarget
|
srvtarget
|
||||||
|
contexts
|
||||||
|
@@ -7,7 +7,8 @@ INCLUDES = \
|
|||||||
-I$(top_srcdir)/gmodule \
|
-I$(top_srcdir)/gmodule \
|
||||||
-I$(top_srcdir)/gobject \
|
-I$(top_srcdir)/gobject \
|
||||||
-I$(top_srcdir)/gio \
|
-I$(top_srcdir)/gio \
|
||||||
$(GLIB_DEBUG_FLAGS)
|
$(GLIB_DEBUG_FLAGS) \
|
||||||
|
-DSRCDIR=\""$(srcdir)"\"
|
||||||
|
|
||||||
noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS)
|
noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS)
|
||||||
progs_ldadd = \
|
progs_ldadd = \
|
||||||
@@ -29,7 +30,8 @@ TEST_PROGS += \
|
|||||||
sleepy-stream \
|
sleepy-stream \
|
||||||
filter-streams \
|
filter-streams \
|
||||||
simple-async-result \
|
simple-async-result \
|
||||||
srvtarget
|
srvtarget \
|
||||||
|
contexts
|
||||||
|
|
||||||
SAMPLE_PROGS = resolver socket-server socket-client echo-server httpd send-data
|
SAMPLE_PROGS = resolver socket-server socket-client echo-server httpd send-data
|
||||||
|
|
||||||
@@ -110,4 +112,8 @@ send_data_LDADD = $(progs_ldadd) \
|
|||||||
srvtarget_SOURCES = srvtarget.c
|
srvtarget_SOURCES = srvtarget.c
|
||||||
srvtarget_LDADD = $(progs_ldadd)
|
srvtarget_LDADD = $(progs_ldadd)
|
||||||
|
|
||||||
|
contexts_SOURCES = contexts.c
|
||||||
|
contexts_LDADD = $(progs_ldadd) \
|
||||||
|
$(top_builddir)/gthread/libgthread-2.0.la
|
||||||
|
|
||||||
DISTCLEAN_FILES = applications/mimeinfo.cache
|
DISTCLEAN_FILES = applications/mimeinfo.cache
|
||||||
|
190
gio/tests/contexts.c
Normal file
190
gio/tests/contexts.c
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
#include <gio/gio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TEST_FILE (SRCDIR "/Makefile.am")
|
||||||
|
char *test_file_buffer;
|
||||||
|
gsize test_file_size;
|
||||||
|
static char async_read_buffer[8192];
|
||||||
|
|
||||||
|
static void
|
||||||
|
read_data (GObject *source, GAsyncResult *result, gpointer loop)
|
||||||
|
{
|
||||||
|
GInputStream *in = G_INPUT_STREAM (source);
|
||||||
|
GError *error = NULL;
|
||||||
|
gssize nread;
|
||||||
|
|
||||||
|
nread = g_input_stream_read_finish (in, result, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
g_assert_cmpint (nread, >, 0);
|
||||||
|
g_assert_cmpint (nread, <=, MIN(sizeof (async_read_buffer), test_file_size));
|
||||||
|
g_assert (memcmp (async_read_buffer, test_file_buffer, nread) == 0);
|
||||||
|
|
||||||
|
g_main_loop_quit (loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
opened_for_read (GObject *source, GAsyncResult *result, gpointer loop)
|
||||||
|
{
|
||||||
|
GFile *file = G_FILE (source);
|
||||||
|
GFileInputStream *in;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
in = g_file_read_finish (file, result, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
memset (async_read_buffer, 0, sizeof (async_read_buffer));
|
||||||
|
g_input_stream_read_async (G_INPUT_STREAM (in),
|
||||||
|
async_read_buffer, sizeof (async_read_buffer),
|
||||||
|
G_PRIORITY_DEFAULT, NULL,
|
||||||
|
read_data, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 1: Async I/O started in a thread with a thread-default context
|
||||||
|
* will stick to that thread, and will complete even if the default
|
||||||
|
* main loop is blocked. (NB: the last part would not be true if we
|
||||||
|
* were testing GFileMonitor!)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean idle_start_test1_thread (gpointer loop);
|
||||||
|
static gpointer test1_thread (gpointer user_data);
|
||||||
|
|
||||||
|
static GCond *test1_cond;
|
||||||
|
static GMutex *test1_mutex;
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_thread_independence (void)
|
||||||
|
{
|
||||||
|
GMainLoop *loop;
|
||||||
|
|
||||||
|
test1_cond = g_cond_new ();
|
||||||
|
test1_mutex = g_mutex_new ();
|
||||||
|
|
||||||
|
loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
g_idle_add (idle_start_test1_thread, loop);
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
g_main_loop_unref (loop);
|
||||||
|
|
||||||
|
g_mutex_free (test1_mutex);
|
||||||
|
g_cond_free (test1_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
idle_start_test1_thread (gpointer loop)
|
||||||
|
{
|
||||||
|
GTimeVal time;
|
||||||
|
GThread *thread;
|
||||||
|
gboolean io_completed;
|
||||||
|
|
||||||
|
g_mutex_lock (test1_mutex);
|
||||||
|
thread = g_thread_create (test1_thread, NULL, TRUE, NULL);
|
||||||
|
|
||||||
|
g_get_current_time (&time);
|
||||||
|
time.tv_sec += 2;
|
||||||
|
io_completed = g_cond_timed_wait (test1_cond, test1_mutex, &time);
|
||||||
|
g_assert (io_completed);
|
||||||
|
g_thread_join (thread);
|
||||||
|
|
||||||
|
g_mutex_unlock (test1_mutex);
|
||||||
|
g_main_loop_quit (loop);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
test1_thread (gpointer user_data)
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
GMainLoop *loop;
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
/* Wait for main thread to be waiting on test1_cond */
|
||||||
|
g_mutex_lock (test1_mutex);
|
||||||
|
g_mutex_unlock (test1_mutex);
|
||||||
|
|
||||||
|
context = g_main_context_new ();
|
||||||
|
g_assert (g_main_context_get_thread_default () == NULL);
|
||||||
|
g_main_context_push_thread_default (context);
|
||||||
|
g_assert (g_main_context_get_thread_default () == context);
|
||||||
|
|
||||||
|
file = g_file_new_for_path (TEST_FILE);
|
||||||
|
g_assert (g_file_supports_thread_contexts (file));
|
||||||
|
|
||||||
|
loop = g_main_loop_new (context, FALSE);
|
||||||
|
g_file_read_async (file, G_PRIORITY_DEFAULT, NULL,
|
||||||
|
opened_for_read, loop);
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
g_main_loop_unref (loop);
|
||||||
|
|
||||||
|
g_cond_signal (test1_cond);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 2: If we push a thread-default context in the main thread, we
|
||||||
|
* can run async ops in that context without running the default
|
||||||
|
* context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean test2_fail (gpointer user_data);
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_context_independence (void)
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
GMainLoop *loop;
|
||||||
|
GFile *file;
|
||||||
|
guint default_timeout;
|
||||||
|
GSource *thread_default_timeout;
|
||||||
|
|
||||||
|
context = g_main_context_new ();
|
||||||
|
g_assert (g_main_context_get_thread_default () == NULL);
|
||||||
|
g_main_context_push_thread_default (context);
|
||||||
|
g_assert (g_main_context_get_thread_default () == context);
|
||||||
|
|
||||||
|
file = g_file_new_for_path (TEST_FILE);
|
||||||
|
g_assert (g_file_supports_thread_contexts (file));
|
||||||
|
|
||||||
|
/* Add a timeout to the main loop, to fail immediately if it gets run */
|
||||||
|
default_timeout = g_timeout_add_full (G_PRIORITY_HIGH, 0,
|
||||||
|
test2_fail, NULL, NULL);
|
||||||
|
/* Add a timeout to the alternate loop, to fail if the I/O *doesn't* run */
|
||||||
|
thread_default_timeout = g_timeout_source_new_seconds (2);
|
||||||
|
g_source_set_callback (thread_default_timeout, test2_fail, NULL, NULL);
|
||||||
|
g_source_attach (thread_default_timeout, context);
|
||||||
|
|
||||||
|
loop = g_main_loop_new (context, FALSE);
|
||||||
|
g_file_read_async (file, G_PRIORITY_DEFAULT, NULL,
|
||||||
|
opened_for_read, loop);
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
g_main_loop_unref (loop);
|
||||||
|
|
||||||
|
g_source_remove (default_timeout);
|
||||||
|
g_source_destroy (thread_default_timeout);
|
||||||
|
g_source_unref (thread_default_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
test2_fail (gpointer user_data)
|
||||||
|
{
|
||||||
|
g_assert_not_reached ();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
g_thread_init (NULL);
|
||||||
|
g_type_init ();
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_file_get_contents (TEST_FILE, &test_file_buffer,
|
||||||
|
&test_file_size, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
g_test_add_func ("/gio/contexts/thread-independence", test_thread_independence);
|
||||||
|
g_test_add_func ("/gio/contexts/context-independence", test_context_independence);
|
||||||
|
|
||||||
|
return g_test_run();
|
||||||
|
}
|
Reference in New Issue
Block a user