[glib] GIOStream: support for unemulated async close()



commit c2c0a6ae5c8a0e924cb4b3a25b6adadcad7bd87e
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Jan 20 08:11:02 2015 -0500

    GIOStream: support for unemulated async close()
    
    Add an implementation of non-thread-emulated async close of a GIOStream
    if either of the underlying stream objects support it.
    
    This prevents us from calling close() functions from another thread on
    an object that may not be expecting that.  It also allows us to skip the
    thread entirely in case our objects support a pure async close.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=741630

 gio/giostream.c |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 78 insertions(+), 3 deletions(-)
---
diff --git a/gio/giostream.c b/gio/giostream.c
index cd177ac..185062f 100644
--- a/gio/giostream.c
+++ b/gio/giostream.c
@@ -26,6 +26,7 @@
 
 #include "giostream.h"
 #include "gasyncresult.h"
+#include "gioprivate.h"
 #include "gtask.h"
 
 /**
@@ -542,6 +543,53 @@ close_async_thread (GTask        *task,
   g_task_return_boolean (task, TRUE);
 }
 
+typedef struct
+{
+  GError *error;
+  gint pending;
+} CloseAsyncData;
+
+static void
+stream_close_complete (GObject      *source,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+  GTask *task = user_data;
+  CloseAsyncData *data;
+
+  data = g_task_get_task_data (task);
+  data->pending--;
+
+  if (G_IS_OUTPUT_STREAM (source))
+    {
+      GError *error = NULL;
+
+      /* Match behaviour with the sync route and give precedent to the
+       * error returned from closing the output stream.
+       */
+      g_output_stream_close_finish (G_OUTPUT_STREAM (source), result, &error);
+      if (error)
+        {
+          if (data->error)
+            g_error_free (data->error);
+          data->error = error;
+        }
+    }
+  else
+    g_input_stream_close_finish (G_INPUT_STREAM (source), result, data->error ? NULL : &data->error);
+
+  if (data->pending == 0)
+    {
+      if (data->error)
+        g_task_return_error (task, data->error);
+      else
+        g_task_return_boolean (task, TRUE);
+
+      g_slice_free (CloseAsyncData, data);
+      g_object_unref (task);
+    }
+}
+
 static void
 g_io_stream_real_close_async (GIOStream           *stream,
                              int                  io_priority,
@@ -549,14 +597,41 @@ g_io_stream_real_close_async (GIOStream           *stream,
                              GAsyncReadyCallback  callback,
                              gpointer             user_data)
 {
+  GInputStream *input;
+  GOutputStream *output;
   GTask *task;
 
   task = g_task_new (stream, cancellable, callback, user_data);
   g_task_set_check_cancellable (task, FALSE);
   g_task_set_priority (task, io_priority);
-  
-  g_task_run_in_thread (task, close_async_thread);
-  g_object_unref (task);
+
+  input = g_io_stream_get_input_stream (stream);
+  output = g_io_stream_get_output_stream (stream);
+
+  if (g_input_stream_async_close_is_via_threads (input) && g_output_stream_async_close_is_via_threads 
(output))
+    {
+      /* No sense in dispatching to the thread twice -- just do it all
+       * in one go.
+       */
+      g_task_run_in_thread (task, close_async_thread);
+      g_object_unref (task);
+    }
+  else
+    {
+      CloseAsyncData *data;
+
+      /* We should avoid dispatching to another thread in case either
+       * object that would not do it for itself because it may not be
+       * threadsafe.
+       */
+      data = g_slice_new (CloseAsyncData);
+      data->error = NULL;
+      data->pending = 2;
+
+      g_task_set_task_data (task, data, NULL);
+      g_input_stream_close_async (input, io_priority, cancellable, stream_close_complete, task);
+      g_output_stream_close_async (output, io_priority, cancellable, stream_close_complete, task);
+    }
 }
 
 static gboolean


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