[wing/wip/poll-stream] Add WingInput/OutputStream classes



commit dc2b41355a5e6562ab03efd618029988675e9ffe
Author: Ignacio Casal Quinteiro <qignacio amazon com>
Date:   Wed Nov 28 15:54:26 2018 +0100

    Add WingInput/OutputStream classes
    
    This is mostly the same as the glib ones with the difference
    that these ones implement the pollable interface. If they result
    to work properly we should merge them back into glib

 wing/meson.build               |   4 +
 wing/wing.h                    |   2 +
 wing/winginputstream.c         | 498 +++++++++++++++++++++++++++++++++++++++++
 wing/winginputstream.h         |  64 ++++++
 wing/wingnamedpipeconnection.c |   8 +-
 wing/wingoutputstream.c        | 466 ++++++++++++++++++++++++++++++++++++++
 wing/wingoutputstream.h        |  65 ++++++
 wing/wingutils.c               |  51 +++++
 wing/wingutils.h               |   8 +
 9 files changed, 1162 insertions(+), 4 deletions(-)
---
diff --git a/wing/meson.build b/wing/meson.build
index a7ca34e..e206e92 100644
--- a/wing/meson.build
+++ b/wing/meson.build
@@ -2,10 +2,12 @@ headers = [
   'wing.h',
   'wingcredentials.h',
   'wingeventwindow.h',
+  'winginputstream.h',
   'wingversionmacros.h',
   'wingnamedpipeclient.h',
   'wingnamedpipeconnection.h',
   'wingnamedpipelistener.h',
+  'wingoutputstream.h',
   'wingservice.h',
   'wingservicemanager.h',
   'wingsource.h',
@@ -16,9 +18,11 @@ sources = [
   'wingcredentials.c',
   'wingeventwindow.c',
   'wing-init.c',
+  'winginputstream.c',
   'wingnamedpipeclient.c',
   'wingnamedpipeconnection.c',
   'wingnamedpipelistener.c',
+  'wingoutputstream.c',
   'wingservice.c',
   'wingservice-private.h',
   'wingservicemanager.c',
diff --git a/wing/wing.h b/wing/wing.h
index 5cc2ed3..f76e237 100644
--- a/wing/wing.h
+++ b/wing/wing.h
@@ -22,9 +22,11 @@
 
 #include <wing/wingversionmacros.h>
 #include <wing/wingeventwindow.h>
+#include <wing/winginputstream.h>
 #include <wing/wingnamedpipeclient.h>
 #include <wing/wingnamedpipelistener.h>
 #include <wing/wingnamedpipeconnection.h>
+#include <wing/wingoutputstream.h>
 #include <wing/wingservice.h>
 #include <wing/wingservicemanager.h>
 #include <wing/wingsource.h>
diff --git a/wing/winginputstream.c b/wing/winginputstream.c
new file mode 100644
index 0000000..1bacf7c
--- /dev/null
+++ b/wing/winginputstream.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ * Copyright (C) 2018 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ * Author: Ignacio Casal Quinteiro <qignacio amazon com>
+ * Author: Silvio Lazzeretti <silviola amazon com>
+ */
+
+#include "winginputstream.h"
+#include "wingutils.h"
+#include "wingsource.h"
+
+#include <windows.h>
+
+/**
+ * SECTION:winginputstream
+ * @short_description: Streaming input operations for Windows file handles
+ * @see_also: #GInputStream
+ *
+ * #WingInputStream implements #GInputStream for reading from a
+ * Windows file handle.
+ */
+
+typedef struct {
+  HANDLE handle;
+  gboolean close_handle;
+
+  OVERLAPPED overlap;
+} WingInputStreamPrivate;
+
+enum {
+  PROP_0,
+  PROP_HANDLE,
+  PROP_CLOSE_HANDLE,
+  LAST_PROP
+};
+
+static GParamSpec *props[LAST_PROP];
+
+G_DEFINE_TYPE_WITH_PRIVATE (WingInputStream, wing_input_stream, G_TYPE_INPUT_STREAM)
+
+static void
+wing_input_stream_finalize (GObject *object)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+
+  wing_stream = WING_INPUT_STREAM (object);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  if (priv->overlap.hEvent != INVALID_HANDLE_VALUE)
+    CloseHandle (priv->overlap.hEvent);
+
+  G_OBJECT_CLASS (wing_input_stream_parent_class)->finalize (object);
+}
+
+static void
+wing_input_stream_set_property (GObject         *object,
+                                guint            prop_id,
+                                const GValue    *value,
+                                GParamSpec      *pspec)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+
+  wing_stream = WING_INPUT_STREAM (object);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      priv->handle = g_value_get_pointer (value);
+      break;
+    case PROP_CLOSE_HANDLE:
+      priv->close_handle = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+wing_input_stream_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+
+  wing_stream = WING_INPUT_STREAM (object);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      g_value_set_pointer (value, priv->handle);
+      break;
+    case PROP_CLOSE_HANDLE:
+      g_value_set_boolean (value, priv->close_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static gssize
+wing_input_stream_read (GInputStream  *stream,
+                        void          *buffer,
+                        gsize          count,
+                        GCancellable  *cancellable,
+                        GError       **error)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+  BOOL res;
+  DWORD nbytes, nread;
+  OVERLAPPED overlap = { 0, };
+  gssize retval = -1;
+
+  wing_stream = WING_INPUT_STREAM (stream);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  overlap.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+  g_return_val_if_fail (overlap.hEvent != NULL, -1);
+
+  res = ReadFile (priv->handle, buffer, nbytes, &nread, &overlap);
+  if (res)
+    retval = nread;
+  else
+    {
+      int errsv = GetLastError ();
+
+      if (errsv == ERROR_IO_PENDING &&
+          wing_overlap_wait_result (priv->handle,
+                                    &overlap, &nread, cancellable))
+        {
+          retval = nread;
+          goto end;
+        }
+
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto end;
+
+      errsv = GetLastError ();
+      if (errsv == ERROR_MORE_DATA)
+        {
+          /* If a named pipe is being read in message mode and the
+           * next message is longer than the nNumberOfBytesToRead
+           * parameter specifies, ReadFile returns FALSE and
+           * GetLastError returns ERROR_MORE_DATA */
+          retval = nread;
+          goto end;
+        }
+      else if (errsv == ERROR_HANDLE_EOF ||
+               errsv == ERROR_BROKEN_PIPE)
+        {
+          /* TODO: the other end of a pipe may call the WriteFile
+           * function with nNumberOfBytesToWrite set to zero. In this
+           * case, it's not possible for the caller to know if it's
+           * broken pipe or a read of 0. Perhaps we should add a
+           * is_broken flag for this win32 case.. */
+          retval = 0;
+        }
+      else
+        {
+          gchar *emsg;
+
+          emsg = g_win32_error_message (errsv);
+          g_set_error (error, G_IO_ERROR,
+                       g_io_error_from_win32_error (errsv),
+                       "Error reading from handle: %s",
+                       emsg);
+          g_free (emsg);
+        }
+    }
+
+end:
+  CloseHandle (overlap.hEvent);
+  return retval;
+}
+
+static gboolean
+wing_input_stream_close (GInputStream  *stream,
+                           GCancellable  *cancellable,
+                           GError       **error)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+  BOOL res;
+
+  wing_stream = WING_INPUT_STREAM (stream);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  if (!priv->close_handle)
+    return TRUE;
+
+  res = CloseHandle (priv->handle);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      g_set_error (error, G_IO_ERROR,
+                   g_io_error_from_win32_error (errsv),
+                   "Error closing handle: %s",
+                   emsg);
+      g_free (emsg);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+read_async_ready (HANDLE   handle,
+                  gpointer user_data)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+  GTask *task = user_data;
+  DWORD nread;
+  gboolean result;
+
+  wing_stream = g_task_get_source_object (task);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  result = GetOverlappedResult (priv->overlap.hEvent, &priv->overlap, &nread, FALSE);
+  if (!result && GetLastError () == ERROR_IO_INCOMPLETE)
+    {
+      /* Try again to wait for the event to get ready */
+      ResetEvent (priv->overlap.hEvent);
+      return G_SOURCE_CONTINUE;
+    }
+
+  ResetEvent (priv->overlap.hEvent);
+
+  g_task_return_int (task, nread);
+  g_object_unref (task);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+wing_input_stream_read_async (GInputStream        *stream,
+                              void                *buffer,
+                              gsize                count,
+                              int                  io_priority,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
+{
+  WingInputStream *wing_stream;
+  WingInputStreamPrivate *priv;
+  BOOL res;
+  DWORD nbytes, nread;
+  int errsv;
+  GTask *task;
+  gchar *emsg;
+
+  wing_stream = WING_INPUT_STREAM (stream);
+  priv = wing_input_stream_get_instance_private (wing_stream);
+
+  task = g_task_new (stream, cancellable, callback, user_data);
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  ResetEvent (priv->overlap.hEvent);
+
+  res = ReadFile (priv->handle, buffer, nbytes, &nread, &priv->overlap);
+  if (res)
+    {
+      ResetEvent (priv->overlap.hEvent);
+      g_task_return_int (task, nread);
+      g_object_unref (task);
+      return;
+    }
+
+  errsv = GetLastError ();
+
+  if (errsv == ERROR_IO_PENDING)
+    {
+      GSource *handle_source;
+
+      handle_source = wing_create_source (priv->overlap.hEvent, G_IO_IN, cancellable);
+      g_task_attach_source (task, handle_source,
+                            (GSourceFunc)read_async_ready);
+      g_source_unref (handle_source);
+      return;
+    }
+
+  if (errsv == ERROR_MORE_DATA)
+    {
+      /* If a named pipe is being read in message mode and the
+       * next message is longer than the nNumberOfBytesToRead
+       * parameter specifies, ReadFile returns FALSE and
+       * GetLastError returns ERROR_MORE_DATA */
+      ResetEvent (priv->overlap.hEvent);
+      g_task_return_int (task, nread);
+      return;
+    }
+
+  if (errsv == ERROR_HANDLE_EOF ||
+      errsv == ERROR_BROKEN_PIPE)
+    {
+      /* TODO: the other end of a pipe may call the WriteFile
+       * function with nNumberOfBytesToWrite set to zero. In this
+       * case, it's not possible for the caller to know if it's
+       * broken pipe or a read of 0. Perhaps we should add a
+       * is_broken flag for this win32 case.. */
+      g_task_return_int (task, 0);
+      return;
+    }
+
+  emsg = g_win32_error_message (errsv);
+  g_task_report_new_error (stream, callback, user_data,
+                           wing_input_stream_read_async,
+                           G_IO_ERROR, g_io_error_from_win32_error (errsv),
+                           "Error reading from handle: %s",
+                           emsg);
+  g_free (emsg);
+}
+
+static void
+wing_input_stream_class_init (WingInputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+  gobject_class->finalize = wing_input_stream_finalize;
+  gobject_class->get_property = wing_input_stream_get_property;
+  gobject_class->set_property = wing_input_stream_set_property;
+
+  stream_class->read_fn = wing_input_stream_read;
+  stream_class->close_fn = wing_input_stream_close;
+  stream_class->read_async = wing_input_stream_read_async;
+
+  /**
+   * WingInputStream:handle:
+   *
+   * The handle that the stream reads from.
+   */
+  props[PROP_HANDLE] =
+    g_param_spec_pointer ("handle",
+                          "File handle",
+                          "The file handle to read from",
+                          G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS);
+
+  /**
+   * WingInputStream:close-handle:
+   *
+   * Whether to close the file handle when the stream is closed.
+   */
+  props[PROP_CLOSE_HANDLE] =
+    g_param_spec_boolean ("close-handle",
+                          "Close file handle",
+                          "Whether to close the file handle when the stream is closed",
+                          TRUE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, LAST_PROP, props);
+}
+
+static void
+wing_input_stream_init (WingInputStream *wing_stream)
+{
+  WingInputStreamPrivate *priv;
+
+  priv = wing_input_stream_get_instance_private (wing_stream);
+  priv->handle = NULL;
+  priv->close_handle = TRUE;
+  priv->overlap.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+  g_return_if_fail (priv->overlap.hEvent != INVALID_HANDLE_VALUE);
+}
+
+/**
+ * wing_input_stream_new:
+ * @handle: a Win32 file handle
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Creates a new #WingInputStream for the given @handle.
+ *
+ * If @close_handle is %TRUE, the handle will be closed
+ * when the stream is closed.
+ *
+ * Note that "handle" here means a Win32 HANDLE, not a "file descriptor"
+ * as used in the Windows C libraries.
+ *
+ * Returns: a new #WingInputStream
+ **/
+GInputStream *
+wing_input_stream_new (void     *handle,
+                          gboolean close_handle)
+{
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  return g_object_new (WING_TYPE_INPUT_STREAM,
+                       "handle", handle,
+                       "close-handle", close_handle,
+                       NULL);
+}
+
+/**
+ * wing_input_stream_set_close_handle:
+ * @stream: a #WingInputStream
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Sets whether the handle of @stream shall be closed
+ * when the stream is closed.
+ */
+void
+wing_input_stream_set_close_handle (WingInputStream *stream,
+                                    gboolean         close_handle)
+{
+  WingInputStreamPrivate *priv;
+
+  g_return_if_fail (WING_IS_INPUT_STREAM (stream));
+
+  priv = wing_input_stream_get_instance_private (stream);
+
+  close_handle = close_handle != FALSE;
+  if (priv->close_handle != close_handle)
+    {
+      priv->close_handle = close_handle;
+      g_object_notify (G_OBJECT (stream), "close-handle");
+    }
+}
+
+/**
+ * wing_input_stream_get_close_handle:
+ * @stream: a #WingInputStream
+ *
+ * Returns whether the handle of @stream will be
+ * closed when the stream is closed.
+ *
+ * Returns: %TRUE if the handle is closed when done
+ */
+gboolean
+wing_input_stream_get_close_handle (WingInputStream *stream)
+{
+  WingInputStreamPrivate *priv;
+
+  g_return_val_if_fail (WING_IS_INPUT_STREAM (stream), FALSE);
+
+  priv = wing_input_stream_get_instance_private (stream);
+
+  return priv->close_handle;
+}
+
+/**
+ * wing_input_stream_get_handle:
+ * @stream: a #WingInputStream
+ *
+ * Return the Windows file handle that the stream reads from.
+ *
+ * Returns: The file handle of @stream
+ */
+void *
+wing_input_stream_get_handle (WingInputStream *stream)
+{
+  WingInputStreamPrivate *priv;
+
+  g_return_val_if_fail (WING_IS_INPUT_STREAM (stream), NULL);
+
+  priv = wing_input_stream_get_instance_private (stream);
+
+  return priv->handle;
+}
diff --git a/wing/winginputstream.h b/wing/winginputstream.h
new file mode 100644
index 0000000..8f88c76
--- /dev/null
+++ b/wing/winginputstream.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ * Copyright (C) 2018 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ * Author: Ignacio Casal Quinteiro <qignacio amazon com>
+ * Author: Silvio Lazzeretti <silviola amazon com>
+ */
+
+#ifndef WING_INPUT_STREAM_H
+#define WING_INPUT_STREAM_H
+
+#include <gio/gio.h>
+#include <wing/wingversionmacros.h>
+
+G_BEGIN_DECLS
+
+#define WING_TYPE_INPUT_STREAM (wing_input_stream_get_type ())
+
+/**
+ * WingInputStream:
+ *
+ * Implements #GInputStream for reading from selectable Windows file handles
+ **/
+WING_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (WingInputStream, wing_input_stream, WING, INPUT_STREAM, GInputStream)
+
+struct _WingInputStreamClass
+{
+  GInputStreamClass parent_class;
+
+  /*< private >*/
+  gpointer padding[10];
+};
+
+WING_AVAILABLE_IN_ALL
+GInputStream * wing_input_stream_new              (void              *handle,
+                                                   gboolean           close_handle);
+WING_AVAILABLE_IN_ALL
+void           wing_input_stream_set_close_handle (WingInputStream *stream,
+                                                   gboolean           close_handle);
+WING_AVAILABLE_IN_ALL
+gboolean       wing_input_stream_get_close_handle (WingInputStream *stream);
+
+WING_AVAILABLE_IN_ALL
+void          *wing_input_stream_get_handle       (WingInputStream *stream);
+
+G_END_DECLS
+
+#endif /* __WING_INPUT_STREAM_H__ */
diff --git a/wing/wingnamedpipeconnection.c b/wing/wingnamedpipeconnection.c
index e3ac37b..00e9cba 100644
--- a/wing/wingnamedpipeconnection.c
+++ b/wing/wingnamedpipeconnection.c
@@ -19,10 +19,10 @@
 
 
 #include "wingnamedpipeconnection.h"
+#include "winginputstream.h"
+#include "wingoutputstream.h"
 
 #include <gio/gio.h>
-#include <gio/gwin32inputstream.h>
-#include <gio/gwin32outputstream.h>
 
 #include <windows.h>
 #include <Sddl.h>
@@ -110,8 +110,8 @@ wing_named_pipe_connection_set_property (GObject      *object,
       connection->handle = g_value_get_pointer (value);
       if (connection->handle != NULL && connection->handle != INVALID_HANDLE_VALUE)
         {
-          connection->input_stream = g_win32_input_stream_new (connection->handle, FALSE);
-          connection->output_stream = g_win32_output_stream_new (connection->handle, FALSE);
+          connection->input_stream = wing_input_stream_new (connection->handle, FALSE);
+          connection->output_stream = wing_output_stream_new (connection->handle, FALSE);
         }
       break;
 
diff --git a/wing/wingoutputstream.c b/wing/wingoutputstream.c
new file mode 100644
index 0000000..ef3c9ba
--- /dev/null
+++ b/wing/wingoutputstream.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ * Copyright (C) 2018 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ * Author: Ignacio Casal Quinteiro <qignacio amazon com>
+ * Author: Silvio Lazzeretti <silviola amazon com>
+ */
+
+#include "wingoutputstream.h"
+#include "wingutils.h"
+#include "wingsource.h"
+
+#include <windows.h>
+
+/**
+ * SECTION:gwin32outputstream
+ * @short_description: Streaming output operations for Windows file handles
+ * @include: gio/gwin32outputstream.h
+ * @see_also: #GOutputStream
+ *
+ * #WingOutputStream implements #GOutputStream for writing to a
+ * Windows file handle.
+ */
+
+typedef struct {
+  HANDLE handle;
+  gboolean close_handle;
+
+  OVERLAPPED overlap;
+} WingOutputStreamPrivate;
+
+enum {
+  PROP_0,
+  PROP_HANDLE,
+  PROP_CLOSE_HANDLE,
+  LAST_PROP
+};
+
+static GParamSpec *props[LAST_PROP];
+
+G_DEFINE_TYPE_WITH_PRIVATE (WingOutputStream, wing_output_stream, G_TYPE_OUTPUT_STREAM)
+
+static void
+wing_output_stream_finalize (GObject *object)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+
+  wing_stream = WING_OUTPUT_STREAM (object);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  if (priv->overlap.hEvent != INVALID_HANDLE_VALUE)
+    CloseHandle (priv->overlap.hEvent);
+
+  G_OBJECT_CLASS (wing_output_stream_parent_class)->finalize (object);
+}
+
+static void
+wing_output_stream_set_property (GObject         *object,
+                                 guint            prop_id,
+                                 const GValue    *value,
+                                 GParamSpec      *pspec)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+
+  wing_stream = WING_OUTPUT_STREAM (object);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      priv->handle = g_value_get_pointer (value);
+      break;
+    case PROP_CLOSE_HANDLE:
+      priv->close_handle = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+wing_output_stream_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+
+  wing_stream = WING_OUTPUT_STREAM (object);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      g_value_set_pointer (value, priv->handle);
+      break;
+    case PROP_CLOSE_HANDLE:
+      g_value_set_boolean (value, priv->close_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static gssize
+wing_output_stream_write (GOutputStream  *stream,
+                          const void     *buffer,
+                          gsize           count,
+                          GCancellable   *cancellable,
+                          GError        **error)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+  BOOL res;
+  DWORD nbytes, nwritten;
+  OVERLAPPED overlap = { 0, };
+  gssize retval = -1;
+
+  wing_stream = WING_OUTPUT_STREAM (stream);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  overlap.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+  g_return_val_if_fail (overlap.hEvent != NULL, -1);
+
+  res = WriteFile (priv->handle, buffer, nbytes, &nwritten, &overlap);
+  if (res)
+    retval = nwritten;
+  else
+    {
+      int errsv = GetLastError ();
+
+      if (errsv == ERROR_IO_PENDING &&
+          wing_overlap_wait_result (priv->handle,
+                                    &overlap, &nwritten, cancellable))
+        {
+          retval = nwritten;
+          goto end;
+        }
+
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto end;
+
+      errsv = GetLastError ();
+      if (errsv == ERROR_HANDLE_EOF ||
+          errsv == ERROR_BROKEN_PIPE)
+        {
+          retval = 0;
+        }
+      else
+        {
+          gchar *emsg;
+
+          emsg = g_win32_error_message (errsv);
+          g_set_error (error, G_IO_ERROR,
+                       g_io_error_from_win32_error (errsv),
+                       "Error writing to handle: %s",
+                       emsg);
+          g_free (emsg);
+        }
+    }
+
+end:
+  CloseHandle (overlap.hEvent);
+  return retval;
+}
+
+static gboolean
+wing_output_stream_close (GOutputStream  *stream,
+                          GCancellable   *cancellable,
+                          GError        **error)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+  BOOL res;
+
+  wing_stream = WING_OUTPUT_STREAM (stream);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  if (!priv->close_handle)
+    return TRUE;
+
+  res = CloseHandle (priv->handle);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      g_set_error (error, G_IO_ERROR,
+                   g_io_error_from_win32_error (errsv),
+                   "Error closing handle: %s",
+                   emsg);
+      g_free (emsg);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+write_async_ready (HANDLE   handle,
+                   gpointer user_data)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+  GTask *task = user_data;
+  DWORD nwritten;
+  gboolean result;
+
+  wing_stream = g_task_get_source_object (task);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  result = GetOverlappedResult (priv->overlap.hEvent, &priv->overlap, &nwritten, FALSE);
+  if (!result && GetLastError () == ERROR_IO_INCOMPLETE)
+    {
+      /* Try again to wait for the event to get ready */
+      ResetEvent (priv->overlap.hEvent);
+      return G_SOURCE_CONTINUE;
+    }
+
+  ResetEvent (priv->overlap.hEvent);
+
+  g_task_return_int (task, nwritten);
+  g_object_unref (task);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+wing_output_stream_write_async (GOutputStream            *stream,
+                                const void               *buffer,
+                                gsize                     count,
+                                int                       io_priority,
+                                GCancellable             *cancellable,
+                                GAsyncReadyCallback       callback,
+                                gpointer                  user_data)
+{
+  WingOutputStream *wing_stream;
+  WingOutputStreamPrivate *priv;
+  BOOL res;
+  DWORD nbytes, nwritten;
+  int errsv;
+  GTask *task;
+  gchar *emsg;
+
+  wing_stream = WING_OUTPUT_STREAM (stream);
+  priv = wing_output_stream_get_instance_private (wing_stream);
+
+  task = g_task_new (stream, cancellable, callback, user_data);
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  ResetEvent (priv->overlap.hEvent);
+
+  res = WriteFile (priv->handle, buffer, nbytes, &nwritten, &priv->overlap);
+  if (res)
+    {
+      ResetEvent (priv->overlap.hEvent);
+      g_task_return_int (task, nwritten);
+      g_object_unref (task);
+      return;
+    }
+
+  errsv = GetLastError ();
+
+  if (errsv == ERROR_IO_PENDING)
+    {
+      GSource *handle_source;
+
+      handle_source = wing_create_source (priv->overlap.hEvent, G_IO_IN, cancellable);
+      g_task_attach_source (task, handle_source,
+                            (GSourceFunc)write_async_ready);
+      g_source_unref (handle_source);
+      return;
+    }
+
+  if (errsv == ERROR_HANDLE_EOF ||
+      errsv == ERROR_BROKEN_PIPE)
+    {
+      g_task_return_int (task, 0);
+      return;
+    }
+
+  emsg = g_win32_error_message (errsv);
+  g_task_report_new_error (stream, callback, user_data,
+                           wing_output_stream_write_async,
+                           G_IO_ERROR, g_io_error_from_win32_error (errsv),
+                           "Error writing to handle: %s",
+                           emsg);
+  g_free (emsg);
+}
+
+static void
+wing_output_stream_class_init (WingOutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+  gobject_class->finalize = wing_output_stream_finalize;
+  gobject_class->get_property = wing_output_stream_get_property;
+  gobject_class->set_property = wing_output_stream_set_property;
+
+  stream_class->write_fn = wing_output_stream_write;
+  stream_class->close_fn = wing_output_stream_close;
+  stream_class->write_async = wing_output_stream_write_async;
+
+   /**
+   * WingOutputStream:handle:
+   *
+   * The file handle that the stream writes to.
+   */
+  props[PROP_HANDLE] =
+    g_param_spec_pointer ("handle",
+                          "File handle",
+                          "The file handle to write to",
+                          G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS);
+
+  /**
+   * WingOutputStream:close-handle:
+   *
+   * Whether to close the file handle when the stream is closed.
+   */
+  props[PROP_CLOSE_HANDLE] =
+    g_param_spec_boolean ("close-handle",
+                          "Close file handle",
+                          "Whether to close the file handle when the stream is closed",
+                          TRUE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, LAST_PROP, props);
+}
+
+static void
+wing_output_stream_init (WingOutputStream *wing_stream)
+{
+  WingOutputStreamPrivate *priv;
+
+  priv = wing_output_stream_get_instance_private (wing_stream);
+  priv->handle = NULL;
+  priv->close_handle = TRUE;
+  priv->overlap.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+  g_return_if_fail (priv->overlap.hEvent != INVALID_HANDLE_VALUE);
+}
+
+/**
+ * wing_output_stream_new:
+ * @handle: a Win32 file handle
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Creates a new #WingOutputStream for the given @handle.
+ *
+ * If @close_handle, is %TRUE, the handle will be closed when the
+ * output stream is destroyed.
+ *
+ * Returns: a new #GOutputStream
+**/
+GOutputStream *
+wing_output_stream_new (void    *handle,
+                        gboolean close_handle)
+{
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  return g_object_new (WING_TYPE_OUTPUT_STREAM,
+                       "handle", handle,
+                       "close-handle", close_handle,
+                       NULL);
+}
+
+/**
+ * wing_output_stream_set_close_handle:
+ * @stream: a #WingOutputStream
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Sets whether the handle of @stream shall be closed when the stream
+ * is closed.
+ */
+void
+wing_output_stream_set_close_handle (WingOutputStream *stream,
+                                     gboolean          close_handle)
+{
+  WingOutputStreamPrivate *priv;
+
+  g_return_if_fail (WING_IS_OUTPUT_STREAM (stream));
+
+  priv = wing_output_stream_get_instance_private (stream);
+
+  close_handle = close_handle != FALSE;
+  if (priv->close_handle != close_handle)
+    {
+      priv->close_handle = close_handle;
+      g_object_notify (G_OBJECT (stream), "close-handle");
+    }
+}
+
+/**
+ * wing_output_stream_get_close_handle:
+ * @stream: a #WingOutputStream
+ *
+ * Returns whether the handle of @stream will be closed when the
+ * stream is closed.
+ *
+ * Returns: %TRUE if the handle is closed when done
+ */
+gboolean
+wing_output_stream_get_close_handle (WingOutputStream *stream)
+{
+  WingOutputStreamPrivate *priv;
+
+  g_return_val_if_fail (WING_IS_OUTPUT_STREAM (stream), FALSE);
+
+  priv = wing_output_stream_get_instance_private (stream);
+
+  return priv->close_handle;
+}
+
+/**
+ * wing_output_stream_get_handle:
+ * @stream: a #WingOutputStream
+ *
+ * Return the Windows handle that the stream writes to.
+ *
+ * Returns: The handle descriptor of @stream
+ */
+void *
+wing_output_stream_get_handle (WingOutputStream *stream)
+{
+  WingOutputStreamPrivate *priv;
+
+  g_return_val_if_fail (WING_IS_OUTPUT_STREAM (stream), NULL);
+
+  priv = wing_output_stream_get_instance_private (stream);
+
+  return priv->handle;
+}
diff --git a/wing/wingoutputstream.h b/wing/wingoutputstream.h
new file mode 100644
index 0000000..71d5921
--- /dev/null
+++ b/wing/wingoutputstream.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ * Copyright (C) 2018 NICE s.r.l.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ * Author: Ignacio Casal Quinteiro <qignacio amazon com>
+ * Author: Silvio Lazzeretti <silviola amazon com>
+ */
+
+#ifndef WING_OUTPUT_STREAM_H
+#define WING_OUTPUT_STREAM_H
+
+#include <gio/gio.h>
+#include <wing/wingversionmacros.h>
+
+G_BEGIN_DECLS
+
+#define WING_TYPE_OUTPUT_STREAM (wing_output_stream_get_type ())
+/**
+ * WingOutputStream:
+ *
+ * Implements #GOutputStream for outputting to Windows file handles
+ **/
+WING_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (WingOutputStream, wing_output_stream, WING, OUTPUT_STREAM, GOutputStream)
+
+struct _WingOutputStreamClass
+{
+  GOutputStreamClass parent_class;
+
+  /*< private >*/
+  gpointer padding[10];
+};
+
+WING_AVAILABLE_IN_ALL
+GOutputStream * wing_output_stream_new              (void             *handle,
+                                                     gboolean          close_handle);
+
+WING_AVAILABLE_IN_ALL
+void            wing_output_stream_set_close_handle (WingOutputStream *stream,
+                                                     gboolean          close_handle);
+
+WING_AVAILABLE_IN_ALL
+gboolean        wing_output_stream_get_close_handle (WingOutputStream *stream);
+
+WING_AVAILABLE_IN_ALL
+void           *wing_output_stream_get_handle       (WingOutputStream *stream);
+
+G_END_DECLS
+
+#endif /* WING_OUTPUT_STREAM_H */
diff --git a/wing/wingutils.c b/wing/wingutils.c
index 1cc1e49..4bdd4a7 100644
--- a/wing/wingutils.c
+++ b/wing/wingutils.c
@@ -127,3 +127,54 @@ wing_get_n_processors (void)
 
   return n > 1 ? (guint)n : 1;
 }
+
+gboolean
+wing_overlap_wait_result (HANDLE           hfile,
+                          OVERLAPPED      *overlap,
+                          DWORD           *transferred,
+                          GCancellable    *cancellable)
+{
+  GPollFD pollfd[2];
+  gboolean result = FALSE;
+  gint num, npoll;
+
+#if GLIB_SIZEOF_VOID_P == 8
+  pollfd[0].fd = (gint64)overlap->hEvent;
+#else
+  pollfd[0].fd = (gint)overlap->hEvent;
+#endif
+  pollfd[0].events = G_IO_IN;
+  num = 1;
+
+  if (g_cancellable_make_pollfd (cancellable, &pollfd[1]))
+    num++;
+
+loop:
+  npoll = g_poll (pollfd, num, -1);
+  if (npoll <= 0)
+    /* error out, should never happen */
+    goto end;
+
+  if (g_cancellable_is_cancelled (cancellable))
+    {
+      /* CancelIO only cancels pending operations issued by the
+       * current thread and since we're doing only sync operations,
+       * this is safe.... */
+      /* CancelIoEx is only Vista+. Since we have only one overlap
+       * operaton on this thread, we can just use: */
+      result = CancelIo (hfile);
+      g_warn_if_fail (result);
+    }
+
+  result = GetOverlappedResult (overlap->hEvent, overlap, transferred, FALSE);
+  if (result == FALSE &&
+      GetLastError () == ERROR_IO_INCOMPLETE &&
+      !g_cancellable_is_cancelled (cancellable))
+    goto loop;
+
+end:
+  if (num > 1)
+    g_cancellable_release_fd (cancellable);
+
+  return result;
+}
diff --git a/wing/wingutils.h b/wing/wingutils.h
index da6653b..8bc0162 100644
--- a/wing/wingutils.h
+++ b/wing/wingutils.h
@@ -19,7 +19,9 @@
 #define WING_UTILS_H
 
 #include <glib.h>
+#include <gio/gio.h>
 #include <wing/wingversionmacros.h>
+#include <windows.h>
 
 G_BEGIN_DECLS
 
@@ -44,6 +46,12 @@ gboolean     wing_get_process_times    (gint64 *current_user_time,
 WING_AVAILABLE_IN_ALL
 guint        wing_get_n_processors     (void);
 
+WING_AVAILABLE_IN_ALL
+gboolean     wing_overlap_wait_result  (HANDLE           hfile,
+                                        OVERLAPPED      *overlap,
+                                        DWORD           *transferred,
+                                        GCancellable    *cancellable);
+
 G_END_DECLS
 
 #endif /* WING_UTILS_H */



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