[glib] Support g_main_context_push_thread_default() in gio



commit 65cc5d895ae125b09f2403761f434fd78ef05af7
Author: Dan Winship <danw gnome org>
Date:   Tue Jun 16 20:22:58 2009 -0400

    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

 gio/gdummyfile.c          |    2 +
 gio/gfile.c               |   25 ++++++
 gio/gfile.h               |    3 +
 gio/gfilemonitor.c        |   26 +++++--
 gio/gio.symbols           |    1 +
 gio/gioscheduler.c        |   29 ++++---
 gio/glocalfile.c          |    2 +
 gio/gsimpleasyncresult.c  |   62 ++++++++++----
 gio/gsocketclient.c       |    2 +-
 gio/gsocketinputstream.c  |    2 +-
 gio/gsocketlistener.c     |    2 +-
 gio/gsocketoutputstream.c |    2 +-
 gio/gtcpconnection.c      |    2 +-
 gio/gunixinputstream.c    |    4 +-
 gio/gunixmount.c          |    4 +-
 gio/gunixoutputstream.c   |    4 +-
 gio/gunixresolver.c       |  207 ++++++++++++++++++++++++++++++---------------
 gio/gunixvolume.c         |    4 +-
 gio/gvolumemonitor.c      |    7 ++-
 gio/gwin32resolver.c      |    4 +-
 gio/tests/.gitignore      |    1 +
 gio/tests/Makefile.am     |   10 ++-
 gio/tests/contexts.c      |  190 +++++++++++++++++++++++++++++++++++++++++
 23 files changed, 474 insertions(+), 121 deletions(-)
---
diff --git a/gio/gdummyfile.c b/gio/gdummyfile.c
index 1033b4e..7d6e763 100644
--- a/gio/gdummyfile.c
+++ b/gio/gdummyfile.c
@@ -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: */
diff --git a/gio/gfile.c b/gio/gfile.c
index 49adf46..1a95f94 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -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"
diff --git a/gio/gfile.h b/gio/gfile.h
index 3707c82..1df47c6 100644
--- a/gio/gfile.h
+++ b/gio/gfile.h
@@ -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__ */
diff --git a/gio/gfilemonitor.c b/gio/gfilemonitor.c
index b24a466..a470baa 100644
--- a/gio/gfilemonitor.c
+++ b/gio/gfilemonitor.c
@@ -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,
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 2323c93..6a11ad9 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -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
 
diff --git a/gio/gioscheduler.c b/gio/gioscheduler.c
index 8d97a56..1787266 100644
--- a/gio/gioscheduler.c
+++ b/gio/gioscheduler.c
@@ -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);
 }
 
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 1f956a5..8ed1e09 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -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;
 }
diff --git a/gio/gsimpleasyncresult.c b/gio/gsimpleasyncresult.c
index e071d96..fe0a6af 100644
--- a/gio/gsimpleasyncresult.c
+++ b/gio/gsimpleasyncresult.c
@@ -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,
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index cfb398f..2a2b862 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -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);
diff --git a/gio/gsocketinputstream.c b/gio/gsocketinputstream.c
index d1fdbc3..046970f 100644
--- a/gio/gsocketinputstream.c
+++ b/gio/gsocketinputstream.c
@@ -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
diff --git a/gio/gsocketlistener.c b/gio/gsocketlistener.c
index f668d14..4050365 100644
--- a/gio/gsocketlistener.c
+++ b/gio/gsocketlistener.c
@@ -765,7 +765,7 @@ g_socket_listener_accept_socket_async (GSocketListener     *listener,
 			       accept_ready,
 			       data,
 			       cancellable,
-			       NULL);
+			       g_main_context_get_thread_default ());
 }
 
 /**
diff --git a/gio/gsocketoutputstream.c b/gio/gsocketoutputstream.c
index 121fb95..7aa9680 100644
--- a/gio/gsocketoutputstream.c
+++ b/gio/gsocketoutputstream.c
@@ -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
diff --git a/gio/gtcpconnection.c b/gio/gtcpconnection.c
index 611c195..de1aa14 100644
--- a/gio/gtcpconnection.c
+++ b/gio/gtcpconnection.c
@@ -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;
diff --git a/gio/gunixinputstream.c b/gio/gunixinputstream.c
index 9a8e8e3..2813aad 100644
--- a/gio/gunixinputstream.c
+++ b/gio/gunixinputstream.c
@@ -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);
 }
 
diff --git a/gio/gunixmount.c b/gio/gunixmount.c
index 8856201..c475ff9 100644
--- a/gio/gunixmount.c
+++ b/gio/gunixmount.c
@@ -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:
diff --git a/gio/gunixoutputstream.c b/gio/gunixoutputstream.c
index 6c11e85..147561b 100644
--- a/gio/gunixoutputstream.c
+++ b/gio/gunixoutputstream.c
@@ -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);
 }
 
diff --git a/gio/gunixresolver.c b/gio/gunixresolver.c
index a1f7425..d25274f 100644
--- a/gio/gunixresolver.c
+++ b/gio/gunixresolver.c
@@ -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,7 +256,8 @@ 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);
 
@@ -261,9 +265,32 @@ resolve_async (GUnixResolver         *gur,
 }
 
 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,
+  req = resolve_async (gur, qy, lookup_by_address_process,
+                       lookup_by_address_free, cancellable,
                        callback, user_data, lookup_by_address_async);
-  req->u.address = g_object_ref (address);
+  req->u.address.address = g_object_ref (address);
 }
 
 static gchar *
@@ -344,26 +392,53 @@ lookup_by_address_finish (GResolver     *resolver,
 {
   GSimpleAsyncResult *simple;
   GUnixResolverRequest *req;
-  gchar host[NI_MAXHOST], *name;
-  gint retval;
+  gchar *name;
 
   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);
 
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
   req = g_simple_async_result_get_op_res_gpointer (simple);
-  retval = _g_asyncns_getnameinfo_done (req->gur->asyncns, req->qy,
-                                        host, sizeof (host), NULL, 0);
-  req->qy = NULL;
-  name = _g_resolver_name_from_nameinfo (req->u.address, host, retval, error);
+  name = req->u.address.hostname;
+  req->u.address.hostname = NULL;
 
   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
 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
@@ -378,9 +453,9 @@ lookup_service_async (GResolver           *resolver,
   _g_asyncns_query_t *qy;
 
   qy = _g_asyncns_res_query (gur->asyncns, rrname, C_IN, T_SRV);
-  req = resolve_async (gur, qy, lookup_service_free, cancellable,
-                       callback, user_data, lookup_service_async);
-  req->u.service = g_strdup (rrname);
+  req = resolve_async (gur, qy, lookup_service_process, lookup_service_free,
+                       cancellable, callback, user_data, lookup_service_async);
+  req->u.service.service = g_strdup (rrname);
 }
 
 static GList *
@@ -390,23 +465,17 @@ lookup_service_finish (GResolver     *resolver,
 {
   GSimpleAsyncResult *simple;
   GUnixResolverRequest *req;
-  guchar *answer;
-  gint len, herr;
   GList *targets;
 
   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);
 
-  req = g_simple_async_result_get_op_res_gpointer (simple);
-  len = _g_asyncns_res_done (req->gur->asyncns, req->qy, &answer);
-  req->qy = NULL;
-  if (len < 0)
-    herr = h_errno;
-  else
-    herr = 0;
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
 
-  targets = _g_resolver_targets_from_res_query (req->u.service, answer, len, herr, error);
-  _g_asyncns_freeanswer (answer);
+  req = g_simple_async_result_get_op_res_gpointer (simple);
+  targets = req->u.service.targets;
+  req->u.service.targets = NULL;
 
   return targets;
 }
diff --git a/gio/gunixvolume.c b/gio/gunixvolume.c
index a1037ab..6f227fc 100644
--- a/gio/gunixvolume.c
+++ b/gio/gunixvolume.c
@@ -420,11 +420,11 @@ eject_mount_do (GVolume             *volume,
   data->error_channel_source = g_io_create_watch (data->error_channel, G_IO_IN);
   g_source_set_callback (data->error_channel_source,
                          (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);
   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);
 
 handle_error:
diff --git a/gio/gvolumemonitor.c b/gio/gvolumemonitor.c
index 168a174..6c7a52d 100644
--- a/gio/gvolumemonitor.c
+++ b/gio/gvolumemonitor.c
@@ -41,7 +41,12 @@
  * #GVolumeMonitor is for listing the user interesting devices and volumes
  * on the computer. In other words, what a file selector or file manager
  * 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);
 
diff --git a/gio/gwin32resolver.c b/gio/gwin32resolver.c
index b02b08c..5198b95 100644
--- a/gio/gwin32resolver.c
+++ b/gio/gwin32resolver.c
@@ -234,7 +234,7 @@ request_cancelled (GCancellable *cancellable,
   req->cancelled_idle = g_idle_source_new ();
   g_source_set_callback (req->cancelled_idle,
                          (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
@@ -479,7 +479,7 @@ g_win32_handle_source_add (HANDLE      handle,
   g_source_add_poll (source, &hsource->pollfd);
 
   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;
 }
 
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index 9295dda..b815e87 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -21,3 +21,4 @@ send-data
 socket-client
 socket-server
 srvtarget
+contexts
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index e49c29e..8b5d30b 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -7,7 +7,8 @@ INCLUDES = 			\
 	-I$(top_srcdir)/gmodule	\
 	-I$(top_srcdir)/gobject	\
 	-I$(top_srcdir)/gio 	\
-	$(GLIB_DEBUG_FLAGS)
+	$(GLIB_DEBUG_FLAGS)	\
+	-DSRCDIR=\""$(srcdir)"\"
 
 noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS)
 progs_ldadd     = 					\
@@ -29,7 +30,8 @@ TEST_PROGS +=	 		\
 	sleepy-stream		\
 	filter-streams		\
 	simple-async-result	\
-	srvtarget
+	srvtarget		\
+	contexts
 
 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_LDADD		  = $(progs_ldadd)
 
+contexts_SOURCES	  = contexts.c
+contexts_LDADD		  = $(progs_ldadd) \
+	$(top_builddir)/gthread/libgthread-2.0.la
+
 DISTCLEAN_FILES = applications/mimeinfo.cache
diff --git a/gio/tests/contexts.c b/gio/tests/contexts.c
new file mode 100644
index 0000000..3fe16cb
--- /dev/null
+++ b/gio/tests/contexts.c
@@ -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();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]