[gtk+/gtk-3-14] wayland: write wl_data_offer data asynchronously



commit 57121ffe2c8d7c980eda22d1034d04b0e155f7ea
Author: Carlos Garnacho <carlosg gnome org>
Date:   Fri Oct 10 20:38:46 2014 +0200

    wayland: write wl_data_offer data asynchronously
    
    Currently writing wl_data_offer data into the fd is 1) synchronous, which
    is noticeable when transferring large amounts of data, and 2) buggy, write()
    error checking is done on the accumulator, breaking both the written data
    accounting and error checking itself.
    
    Fix both by making writes asynchonous through GOutputStream, the operation
    is spun off and either finished, or cancelled if new data is stored in the
    selection while the transfer is active.

 gdk/wayland/gdkselection-wayland.c |  114 ++++++++++++++++++++++++++++++-----
 1 files changed, 97 insertions(+), 17 deletions(-)
---
diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c
index 98c6930..52d8535 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 
 #include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
 #include <glib-unix.h>
 
 #include "gdkwayland.h"
@@ -33,6 +34,7 @@
 
 typedef struct _SelectionBuffer SelectionBuffer;
 typedef struct _StoredSelection StoredSelection;
+typedef struct _AsyncWriteData AsyncWriteData;
 
 struct _SelectionBuffer
 {
@@ -48,6 +50,7 @@ struct _SelectionBuffer
 struct _StoredSelection
 {
   GdkWindow *source;
+  GCancellable *cancellable;
   guchar *data;
   gsize data_len;
   GdkAtom type;
@@ -60,6 +63,12 @@ struct _DataSourceData
   GdkAtom selection;
 };
 
+struct _AsyncWriteData {
+  GOutputStream *stream;
+  GdkWaylandSelection *selection;
+  gsize index;
+};
+
 enum {
   ATOM_CLIPBOARD,
   ATOM_DND
@@ -87,6 +96,7 @@ struct _GdkWaylandSelection
 };
 
 static void selection_buffer_read (SelectionBuffer *buffer);
+static void async_write_data_write (AsyncWriteData *write_data);
 
 static void
 selection_buffer_notify (SelectionBuffer *buffer)
@@ -272,6 +282,12 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection)
 
   g_free (selection->stored_selection.data);
 
+  if (selection->stored_selection.cancellable)
+    {
+      g_cancellable_cancel (selection->stored_selection.cancellable);
+      g_object_unref (selection->stored_selection.cancellable);
+    }
+
   if (selection->stored_selection.fd > 0)
     close (selection->stored_selection.fd);
 
@@ -366,34 +382,91 @@ gdk_wayland_selection_emit_request (GdkWindow *window,
   gdk_event_free (event);
 }
 
-static gboolean
-gdk_wayland_selection_check_write (GdkWaylandSelection *selection)
+static AsyncWriteData *
+async_write_data_new (GdkWaylandSelection *selection)
 {
-  gssize len, bytes_written = 0;
-  gchar *buf;
+  AsyncWriteData *write_data;
 
-  if (selection->stored_selection.fd < 0 ||
-      selection->stored_selection.data_len == 0)
-    return FALSE;
+  write_data = g_slice_new0 (AsyncWriteData);
+  write_data->selection = selection;
+  write_data->stream =
+    g_unix_output_stream_new (selection->stored_selection.fd, TRUE);
+
+  return write_data;
+}
 
-  len = selection->stored_selection.data_len;
-  buf = (gchar *) selection->stored_selection.data;
+static void
+async_write_data_free (AsyncWriteData *write_data)
+{
+  g_object_unref (write_data->stream);
+  g_slice_free (AsyncWriteData, write_data);
+}
 
-  while (len > 0)
+static void
+async_write_data_cb (GObject      *object,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+  AsyncWriteData *write_data = user_data;
+  GError *error = NULL;
+  gsize bytes_written;
+
+  bytes_written = g_output_stream_write_finish (G_OUTPUT_STREAM (object),
+                                                res, &error);
+  if (error)
     {
-      bytes_written += write (selection->stored_selection.fd,
-                              buf + bytes_written, len);
+      g_warning ("Error writing selection data: %s", error->message);
+      g_error_free (error);
 
-      if (bytes_written < 0)
-        break;
+      async_write_data_free (write_data);
+      return;
+    }
+
+  write_data->index += bytes_written;
 
-      len -= bytes_written;
+  if (write_data->index <
+      write_data->selection->stored_selection.data_len)
+    {
+      /* Write the next chunk */
+      async_write_data_write (write_data);
     }
+  else
+    async_write_data_free (write_data);
+}
 
-  close (selection->stored_selection.fd);
+static void
+async_write_data_write (AsyncWriteData *write_data)
+{
+  GdkWaylandSelection *selection = write_data->selection;
+  gsize buf_len;
+  guchar *buf;
+
+  buf = selection->stored_selection.data;
+  buf_len = selection->stored_selection.data_len;
+
+  g_output_stream_write_async (write_data->stream,
+                               &buf[write_data->index],
+                               buf_len - write_data->index,
+                               G_PRIORITY_DEFAULT,
+                               selection->stored_selection.cancellable,
+                               async_write_data_cb,
+                               write_data);
+}
+
+static gboolean
+gdk_wayland_selection_check_write (GdkWaylandSelection *selection)
+{
+  AsyncWriteData *write_data;
+
+  if (selection->stored_selection.fd < 0 ||
+      selection->stored_selection.data_len == 0)
+    return FALSE;
+
+  write_data = async_write_data_new (selection);
+  async_write_data_write (write_data);
   selection->stored_selection.fd = -1;
 
-  return bytes_written != 0;
+  return TRUE;
 }
 
 void
@@ -435,10 +508,17 @@ gdk_wayland_selection_store (GdkWindow    *window,
       g_free (selection->stored_selection.data);
     }
 
+  if (selection->stored_selection.cancellable)
+    {
+      g_cancellable_cancel (selection->stored_selection.cancellable);
+      g_object_unref (selection->stored_selection.cancellable);
+    }
+
   selection->stored_selection.source = window;
   selection->stored_selection.data_len = array->len;
   selection->stored_selection.data = (guchar *) g_array_free (array, FALSE);
   selection->stored_selection.type = type;
+  selection->stored_selection.cancellable = g_cancellable_new ();
 
   gdk_wayland_selection_check_write (selection);
 }


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