Bug 562452 - Ensure we return G_IO_ERROR_CANCELLED if cancelling
authorAlexander Larsson <alexl@redhat.com>
Wed, 10 Dec 2008 13:25:59 +0000 (13:25 +0000)
committerAlexander Larsson <alexl@src.gnome.org>
Wed, 10 Dec 2008 13:25:59 +0000 (13:25 +0000)
2008-12-10  Alexander Larsson  <alexl@redhat.com>

Bug 562452 - Ensure we return G_IO_ERROR_CANCELLED if cancelling
g_simple_async_result_run_in_thread

        * gsimpleasyncresult.c:
Make g_simple_async_result_run_in_thread check cancellation before
calling out to the user in the callback. This means we guarantee
reporting cancels of async operations from the main threads, which
is probably more in line with what users expect.

Note that there are still no such guarantees for cancelling sync
operations or cancelling async operation from outside the main
thread. Furthermore, the exact behaviour of async implementations
not using run_in_thread may differ.

svn path=/trunk/; revision=7740

gio/ChangeLog
gio/gsimpleasyncresult.c

index 5a071f2..fb8d548 100644 (file)
@@ -1,3 +1,19 @@
+2008-12-10  Alexander Larsson  <alexl@redhat.com>
+
+       Bug 562452 - Ensure we return G_IO_ERROR_CANCELLED if cancelling
+       g_simple_async_result_run_in_thread
+       
+        * gsimpleasyncresult.c:
+       Make g_simple_async_result_run_in_thread check cancellation before
+       calling out to the user in the callback. This means we guarantee
+       reporting cancels of async operations from the main threads, which
+       is probably more in line with what users expect.
+       
+       Note that there are still no such guarantees for cancelling sync
+       operations or cancelling async operation from outside the main
+       thread. Furthermore, the exact behaviour of async implementations
+       not using run_in_thread may differ.
+
 2008-12-09  Alexander Larsson  <alexl@redhat.com>
 
        Bug 515777 - incorrect date&time on copy
index 2d20f78..c3c1ae0 100644 (file)
@@ -83,7 +83,9 @@
  * 
  * An asynchronous operation can be made to ignore a cancellation event by 
  * calling g_simple_async_result_set_handle_cancellation() with a 
- * #GSimpleAsyncResult for the operation and %FALSE. 
+ * #GSimpleAsyncResult for the operation and %FALSE. This is useful for
+ * operations that are dangerous to cancel, such as close (which would
+ * cause a leak if cancelled before being run).
  * 
  * GSimpleAsyncResult can integrate into GLib's event loop, #GMainLoop, 
  * or it can use #GThread<!-- -->s if available. 
@@ -464,6 +466,8 @@ g_simple_async_result_set_from_error (GSimpleAsyncResult *simple,
   g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
   g_return_if_fail (error != NULL);
 
+  if (simple->error)
+    g_error_free (simple->error);
   simple->error = g_error_copy (error);
   simple->failed = TRUE;
 }
@@ -507,6 +511,8 @@ g_simple_async_result_set_error_va (GSimpleAsyncResult *simple,
   g_return_if_fail (domain != 0);
   g_return_if_fail (format != NULL);
 
+  if (simple->error)
+    g_error_free (simple->error);
   simple->error = _g_error_new_valist (domain, code, format, args);
   simple->failed = TRUE;
 }
@@ -544,6 +550,9 @@ g_simple_async_result_set_error (GSimpleAsyncResult *simple,
  * @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().
  **/
 void
 g_simple_async_result_complete (GSimpleAsyncResult *simple)
@@ -593,9 +602,36 @@ g_simple_async_result_complete_in_idle (GSimpleAsyncResult *simple)
 
 typedef struct {
   GSimpleAsyncResult *simple;
+  GCancellable *cancellable;
   GSimpleAsyncThreadFunc func;
 } RunInThreadData;
 
+
+static gboolean
+complete_in_idle_cb_for_thread (gpointer _data)
+{
+  RunInThreadData *data = _data;
+  GSimpleAsyncResult *simple;
+
+  simple = data->simple;
+  
+  if (simple->handle_cancellation &&
+      g_cancellable_is_cancelled (data->cancellable))
+    g_simple_async_result_set_error (simple,
+                                     G_IO_ERROR,
+                                     G_IO_ERROR_CANCELLED,
+                                     "%s", _("Operation was cancelled"));
+  
+  g_simple_async_result_complete (simple);
+
+  if (data->cancellable)
+    g_object_unref (data->cancellable);
+  g_object_unref (data->simple);
+  g_free (data);
+  
+  return FALSE;
+}
+
 static gboolean
 run_in_thread (GIOSchedulerJob *job,
                GCancellable    *c,
@@ -603,7 +639,9 @@ 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))
     g_simple_async_result_set_error (simple,
@@ -615,9 +653,12 @@ run_in_thread (GIOSchedulerJob *job,
                 simple->source_object,
                 c);
 
-  g_simple_async_result_complete_in_idle (data->simple);
-  g_object_unref (data->simple);
-  g_free (data);
+  source = g_idle_source_new ();
+  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_unref (source);
 
   return FALSE;
 }
@@ -645,6 +686,9 @@ g_simple_async_result_run_in_thread (GSimpleAsyncResult     *simple,
   data = g_new (RunInThreadData, 1);
   data->func = func;
   data->simple = g_object_ref (simple);
+  data->cancellable = cancellable;
+  if (cancellable)
+    g_object_ref (cancellable);
   g_io_scheduler_push_job (run_in_thread, data, NULL, io_priority, cancellable);
 }