[glib] Add GWin32InputStream and GWin32OutputStream classes



commit 9af8b8321146cddbe5bee09e972507f023e58ce0
Author: Tor Lillqvist <tml iki fi>
Date:   Mon Apr 19 11:32:05 2010 +0300

    Add GWin32InputStream and GWin32OutputStream classes
    
    Correspond to GUnixInputStream and GUnixOutputStream. No true async
    support though. But that is how the Win32 API is, for files not
    explicitly opened for so-called overlapped IO.
    
    The API to create these streams takes Win32 HANDLEs. Not file
    descriptors, because file descriptors are specific to the C library
    used. The user code and GLib might be using different C libraries.
    
    Also add a test program for the new classes, and a gio-windows-2.0.pc
    file.

 Makefile.am                  |    5 +
 build/win32/vs9/glib.vsprops |    4 +
 configure.in                 |    1 +
 gio-windows-2.0.pc.in        |   11 ++
 gio/Makefile.am              |   11 ++-
 gio/gio.symbols              |   27 +++
 gio/gioerror.c               |   27 +++
 gio/gioerror.h               |    4 +
 gio/gwin32inputstream.c      |  359 ++++++++++++++++++++++++++++++++++++++++++
 gio/gwin32inputstream.h      |   79 +++++++++
 gio/gwin32outputstream.c     |  359 ++++++++++++++++++++++++++++++++++++++++++
 gio/gwin32outputstream.h     |   78 +++++++++
 gio/tests/Makefile.am        |    8 +
 gio/tests/win32-streams.c    |  292 ++++++++++++++++++++++++++++++++++
 glib-zip.in                  |    1 +
 15 files changed, 1265 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 708b16b..ace9c9a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,7 @@ EXTRA_DIST += 			\
 	gthread-2.0.pc.in	\
 	gio-2.0.pc.in		\
 	gio-unix-2.0.pc.in	\
+	gio-windows-2.0.pc.in	\
 	glib-2.0-uninstalled.pc.in 	\
 	gobject-2.0-uninstalled.pc.in 	\
 	gmodule-2.0-uninstalled.pc.in	\
@@ -108,6 +109,10 @@ if OS_UNIX
 pkgconfig_DATA += gio-unix-2.0.pc
 endif
 
+if OS_WIN32
+pkgconfig_DATA += gio-windows-2.0.pc
+endif
+
 $(pkgconfig_DATA): config.status
 
 # install mkinstalldirs for glib-gettextize's benefit
diff --git a/build/win32/vs9/glib.vsprops b/build/win32/vs9/glib.vsprops
index af95dc8..d562434 100644
--- a/build/win32/vs9/glib.vsprops
+++ b/build/win32/vs9/glib.vsprops
@@ -204,6 +204,10 @@ copy ..\..\..\gio\gvolumemonitor.h $(OutDir)\include\glib-2.0\gio&#x0D;&#x0A;
 copy ..\..\..\gio\gzlibcompressor.h $(OutDir)\include\glib-2.0\gio&#x0D;&#x0A;
 copy ..\..\..\gio\gzlibdecompressor.h $(OutDir)\include\glib-2.0\gio&#x0D;&#x0A;
 
+mkdir $(OutDir)\include\gio-win32-2.0\gio&#x0D;&#x0A;
+copy ..\..\..\gio\gwin32inputstream.h $(OutDir)\include\gio-win32-2.0\gio&#x0D;&#x0A;
+copy ..\..\..\gio\gwin32outputstream.h $(OutDir)\include\gio-win32-2.0\gio&#x0D;&#x0A;
+
 mkdir $(OutDir)\lib\glib-2.0\include&#x0D;&#x0A;
 copy ..\..\..\glibconfig.h $(OutDir)\lib\glib-2.0\include&#x0D;&#x0A;
 
diff --git a/configure.in b/configure.in
index 759adf6..5001af6 100644
--- a/configure.in
+++ b/configure.in
@@ -3510,6 +3510,7 @@ gobject-2.0.pc
 gobject-2.0-uninstalled.pc
 gio-2.0.pc
 gio-unix-2.0.pc
+gio-windows-2.0.pc
 gio-2.0-uninstalled.pc
 gio-unix-2.0-uninstalled.pc
 glib-zip
diff --git a/gio-windows-2.0.pc.in b/gio-windows-2.0.pc.in
new file mode 100644
index 0000000..77eecdf
--- /dev/null
+++ b/gio-windows-2.0.pc.in
@@ -0,0 +1,11 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: GIO Windows specific APIs
+Description: Windows specific headers for glib I/O library
+Version: @VERSION@
+Requires: gobject-2.0,gmodule-no-export-2.0,gio-2.0
+Libs: -L${libdir} -lgio-2.0
+Cflags: -I${includedir}/gio-win32-2.0/
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 9749b3e..8e3c185 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -202,6 +202,15 @@ win32_sources = \
 	gwin32resolver.h \
 	gwin32volumemonitor.c \
 	gwin32volumemonitor.h \
+	gwin32inputstream.c \
+	gwin32outputstream.c \
+	gwin32outputstream.h \
+	$(NULL)
+
+giowin32includedir=$(includedir)/gio-win32-2.0/gio
+giowin32include_HEADERS = \
+	gwin32inputstream.h \
+	gwin32outputstream.h \
 	$(NULL)
 
 endif
@@ -509,7 +518,7 @@ dist-hook: $(BUILT_EXTRA_DIST) ../build/win32/vs9/gio.vcproj
 	  cp $$d/$$f $(distdir) || exit 1; done
 
 ../build/win32/vs9/gio.vcproj: $(top_srcdir)/build/win32/vs9/gio.vcprojin
-	for F in $(libgio_2_0_la_SOURCES); do \
+	for F in $(libgio_2_0_la_SOURCES) $(win32_sources); do \
 		case $$F in \
 		*.c) echo '   <File RelativePath="..\..\..\gio\'$$F'" />' \
 		     ;; \
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 42a569d..cedfd39 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -611,6 +611,9 @@ g_io_stream_clear_pending
 #if IN_FILE(__G_IO_ERROR_C__)
 g_io_error_quark
 g_io_error_from_errno
+#ifdef G_OS_WIN32
+g_io_error_from_win32_error
+#endif
 #endif
 #endif
 
@@ -827,6 +830,30 @@ g_unix_output_stream_get_fd
 #endif
 #endif
 
+#if IN_HEADER(__G_WIN32_INPUT_STREAM_H__)
+#if IN_FILE(__G_WIN32_INPUT_STREAM_C__)
+#ifdef G_OS_WIN32
+g_win32_input_stream_get_type  G_GNUC_CONST
+g_win32_input_stream_new
+g_win32_input_stream_set_close_handle
+g_win32_input_stream_get_close_handle
+g_win32_input_stream_get_handle
+#endif /* G_OS_WIN32 */
+#endif
+#endif
+
+#if IN_HEADER(__G_WIN32_OUTPUT_STREAM_H__)
+#if IN_FILE(__G_WIN32_OUTPUT_STREAM_C__)
+#ifdef G_OS_WIN32
+g_win32_output_stream_get_type  G_GNUC_CONST
+g_win32_output_stream_new
+g_win32_output_stream_set_close_handle
+g_win32_output_stream_get_close_handle
+g_win32_output_stream_get_handle
+#endif /* G_OS_WIN32 */
+#endif
+#endif
+
 #if IN_HEADER(__G_MOUNT_H__)
 #if IN_FILE(__G_MOUNT_C__)
 g_mount_get_type  G_GNUC_CONST
diff --git a/gio/gioerror.c b/gio/gioerror.c
index 287cb2b..8a1ee4f 100644
--- a/gio/gioerror.c
+++ b/gio/gioerror.c
@@ -206,5 +206,32 @@ g_io_error_from_errno (gint err_no)
     }
 }
 
+#ifdef G_OS_WIN32
+
+/**
+ * g_io_error_from_win32_error:
+ * @error_code: Windows error number.
+ *
+ * Converts some common error codes into GIO error codes. The
+ * fallback value G_IO_ERROR_FAILED is returned for error codes not
+ * handled.
+ *
+ * Returns: #GIOErrorEnum value for the given error number.
+ *
+ * Since: 2.26
+ **/
+GIOErrorEnum
+g_io_error_from_win32_error (gint error_code)
+{
+  switch (error_code)
+    {
+    default:
+      return G_IO_ERROR_FAILED;
+      break;
+    }
+}
+
+#endif
+
 #define __G_IO_ERROR_C__
 #include "gioaliasdef.c"
diff --git a/gio/gioerror.h b/gio/gioerror.h
index c1dc377..12c2b06 100644
--- a/gio/gioerror.h
+++ b/gio/gioerror.h
@@ -43,6 +43,10 @@ G_BEGIN_DECLS
 GQuark       g_io_error_quark      (void);
 GIOErrorEnum g_io_error_from_errno (gint err_no);
 
+#ifdef G_OS_WIN32
+GIOErrorEnum g_io_error_from_win32_error (gint error_code);
+#endif
+
 G_END_DECLS
 
 #endif /* __G_IO_ERROR_H__ */
diff --git a/gio/gwin32inputstream.c b/gio/gwin32inputstream.c
new file mode 100644
index 0000000..3cfe5cc
--- /dev/null
+++ b/gio/gwin32inputstream.c
@@ -0,0 +1,359 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ */
+
+#include "config.h"
+
+#include <windows.h>
+
+#include <io.h>
+
+#include <glib.h>
+#include "gioerror.h"
+#include "gsimpleasyncresult.h"
+#include "gwin32inputstream.h"
+#include "gcancellable.h"
+#include "gasynchelper.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gwin32inputstream
+ * @short_description: Streaming input operations for Windows file handles
+ * @include: gio/gwin32inputstream.h
+ * @see_also: #GInputStream
+ *
+ * #GWin32InputStream implements #GInputStream for reading from a
+ * Windows file handle.
+ *
+ * Note that <filename>&lt;gio/gwin32inputstream.h&gt;</filename> belongs
+ * to the Windows-specific GIO interfaces, thus you have to use the
+ * <filename>gio-windows-2.0.pc</filename> pkg-config file when using it.
+ */
+
+enum {
+  PROP_0,
+  PROP_HANDLE,
+  PROP_CLOSE_HANDLE
+};
+
+G_DEFINE_TYPE (GWin32InputStream, g_win32_input_stream, G_TYPE_INPUT_STREAM);
+
+struct _GWin32InputStreamPrivate {
+  HANDLE handle;
+  gboolean close_handle;
+};
+
+static void     g_win32_input_stream_set_property (GObject              *object,
+						   guint                 prop_id,
+						   const GValue         *value,
+						   GParamSpec           *pspec);
+static void     g_win32_input_stream_get_property (GObject              *object,
+						   guint                 prop_id,
+						   GValue               *value,
+						   GParamSpec           *pspec);
+static gssize   g_win32_input_stream_read         (GInputStream         *stream,
+						   void                 *buffer,
+						   gsize                 count,
+						   GCancellable         *cancellable,
+						   GError              **error);
+static gboolean g_win32_input_stream_close        (GInputStream         *stream,
+						   GCancellable         *cancellable,
+						   GError              **error);
+
+static void
+g_win32_input_stream_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (g_win32_input_stream_parent_class)->finalize (object);
+}
+
+static void
+g_win32_input_stream_class_init (GWin32InputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GWin32InputStreamPrivate));
+
+  gobject_class->get_property = g_win32_input_stream_get_property;
+  gobject_class->set_property = g_win32_input_stream_set_property;
+  gobject_class->finalize = g_win32_input_stream_finalize;
+
+  stream_class->read_fn = g_win32_input_stream_read;
+  stream_class->close_fn = g_win32_input_stream_close;
+
+  /**
+   * GWin32InputStream:handle:
+   *
+   * The handle that the stream reads from.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+				   PROP_HANDLE,
+				   g_param_spec_pointer ("handle",
+							 P_("File handle"),
+							 P_("The file handle to read from"),
+							 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  /**
+   * GWin32InputStream:close-handle:
+   *
+   * Whether to close the file handle when the stream is closed.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+				   PROP_CLOSE_HANDLE,
+				   g_param_spec_boolean ("close-handle",
+							 P_("Close file handle"),
+							 P_("Whether to close the file handle when the stream is closed"),
+							 TRUE,
+							 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+}
+
+static void
+g_win32_input_stream_set_property (GObject         *object,
+				   guint            prop_id,
+				   const GValue    *value,
+				   GParamSpec      *pspec)
+{
+  GWin32InputStream *win32_stream;
+
+  win32_stream = G_WIN32_INPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      win32_stream->priv->handle = g_value_get_pointer (value);
+      break;
+    case PROP_CLOSE_HANDLE:
+      win32_stream->priv->close_handle = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_win32_input_stream_get_property (GObject    *object,
+				   guint       prop_id,
+				   GValue     *value,
+				   GParamSpec *pspec)
+{
+  GWin32InputStream *win32_stream;
+
+  win32_stream = G_WIN32_INPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      g_value_set_pointer (value, win32_stream->priv->handle);
+      break;
+    case PROP_CLOSE_HANDLE:
+      g_value_set_boolean (value, win32_stream->priv->close_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_win32_input_stream_init (GWin32InputStream *win32_stream)
+{
+  win32_stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (win32_stream,
+						    G_TYPE_WIN32_INPUT_STREAM,
+						    GWin32InputStreamPrivate);
+
+  win32_stream->priv->handle = NULL;
+  win32_stream->priv->close_handle = TRUE;
+}
+
+/**
+ * g_win32_input_stream_new:
+ * @handle: a Win32 file handle
+ * @close_fd: %TRUE to close the handle when done
+ *
+ * Creates a new #GWin32InputStream for the given @fd.
+ *
+ * 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 #GWin32InputStream
+ **/
+GInputStream *
+g_win32_input_stream_new (void     *handle,
+			  gboolean close_handle)
+{
+  GWin32InputStream *stream;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  stream = g_object_new (G_TYPE_WIN32_INPUT_STREAM,
+			 "handle", handle,
+			 "close-handle", close_handle,
+			 NULL);
+
+  return G_INPUT_STREAM (stream);
+}
+
+/**
+ * g_win32_input_stream_set_close_handle:
+ * @stream: a #GWin32InputStream
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Sets whether the handle of @stream shall be closed
+ * when the stream is closed.
+ *
+ * Since: 2.26
+ */
+void
+g_win32_input_stream_set_close_handle (GWin32InputStream *stream,
+				       gboolean          close_handle)
+{
+  g_return_if_fail (G_IS_WIN32_INPUT_STREAM (stream));
+
+  close_handle = close_handle != FALSE;
+  if (stream->priv->close_handle != close_handle)
+    {
+      stream->priv->close_handle = close_handle;
+      g_object_notify (G_OBJECT (stream), "close-handle");
+    }
+}
+
+/**
+ * g_win32_input_stream_get_close_handle:
+ * @stream: a #GWin32InputStream
+ *
+ * Returns whether the handle of @stream will be
+ * closed when the stream is closed.
+ *
+ * Return value: %TRUE if the handle is closed when done
+ *
+ * Since: 2.26
+ */
+gboolean
+g_win32_input_stream_get_close_handle (GWin32InputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), FALSE);
+
+  return stream->priv->close_handle;
+}
+
+/**
+ * g_win32_input_stream_get_handle:
+ * @stream: a #GWin32InputStream
+ *
+ * Return the Windows file handle that the stream reads from.
+ *
+ * Return value: The file handle of @stream
+ *
+ * Since: 2.26
+ */
+void *
+g_win32_input_stream_get_handle (GWin32InputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), NULL);
+
+  return stream->priv->handle;
+}
+
+static gssize
+g_win32_input_stream_read (GInputStream  *stream,
+			   void          *buffer,
+			   gsize          count,
+			   GCancellable  *cancellable,
+			   GError       **error)
+{
+  GWin32InputStream *win32_stream;
+  BOOL res;
+  DWORD nbytes, nread;
+
+  win32_stream = G_WIN32_INPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  res = ReadFile (win32_stream->priv->handle, buffer, nbytes, &nread, NULL);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg;
+
+      if (errsv == ERROR_HANDLE_EOF ||
+	  errsv == ERROR_BROKEN_PIPE)
+	return 0;
+
+      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);
+      return -1;
+    }
+
+  return nread;
+}
+
+static gboolean
+g_win32_input_stream_close (GInputStream  *stream,
+			   GCancellable  *cancellable,
+			   GError       **error)
+{
+  GWin32InputStream *win32_stream;
+  BOOL res;
+
+  win32_stream = G_WIN32_INPUT_STREAM (stream);
+
+  if (!win32_stream->priv->close_handle)
+    return TRUE;
+
+  res = CloseHandle (win32_stream->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;
+}
+
+#define __G_WIN32_INPUT_STREAM_C__
+#include "gioaliasdef.c"
diff --git a/gio/gwin32inputstream.h b/gio/gwin32inputstream.h
new file mode 100644
index 0000000..937685f
--- /dev/null
+++ b/gio/gwin32inputstream.h
@@ -0,0 +1,79 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ */
+
+#ifndef __G_WIN32_INPUT_STREAM_H__
+#define __G_WIN32_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_INPUT_STREAM         (g_win32_input_stream_get_type ())
+#define G_WIN32_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_INPUT_STREAM, GWin32InputStream))
+#define G_WIN32_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_INPUT_STREAM, GWin32InputStreamClass))
+#define G_IS_WIN32_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_INPUT_STREAM))
+#define G_IS_WIN32_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_INPUT_STREAM))
+#define G_WIN32_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_INPUT_STREAM, GWin32InputStreamClass))
+
+/**
+ * GWin32InputStream:
+ *
+ * Implements #GInputStream for reading from selectable Windows file handles
+ **/
+typedef struct _GWin32InputStream         GWin32InputStream;
+typedef struct _GWin32InputStreamClass    GWin32InputStreamClass;
+typedef struct _GWin32InputStreamPrivate  GWin32InputStreamPrivate;
+
+struct _GWin32InputStream
+{
+  GInputStream parent_instance;
+
+  /*< private >*/
+  GWin32InputStreamPrivate *priv;
+};
+
+struct _GWin32InputStreamClass
+{
+  GInputStreamClass parent_class;
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType          g_win32_input_stream_get_type         (void) G_GNUC_CONST;
+
+GInputStream * g_win32_input_stream_new              (void              *handle,
+						      gboolean           close_handle);
+void           g_win32_input_stream_set_close_handle (GWin32InputStream *stream,
+						      gboolean           close_handle);
+gboolean       g_win32_input_stream_get_close_handle (GWin32InputStream *stream);
+void          *g_win32_input_stream_get_handle       (GWin32InputStream *stream);
+
+G_END_DECLS
+
+#endif /* __G_WIN32_INPUT_STREAM_H__ */
diff --git a/gio/gwin32outputstream.c b/gio/gwin32outputstream.c
new file mode 100644
index 0000000..7b9f03b
--- /dev/null
+++ b/gio/gwin32outputstream.c
@@ -0,0 +1,359 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ */
+
+#include "config.h"
+
+#include <windows.h>
+
+#include <io.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "gioerror.h"
+#include "gwin32outputstream.h"
+#include "gcancellable.h"
+#include "gsimpleasyncresult.h"
+#include "gasynchelper.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gwin32outputstream
+ * @short_description: Streaming output operations for Windows file handles
+ * @include: gio/gwin32outputstream.h
+ * @see_also: #GOutputStream
+ *
+ * #GWin32OutputStream implements #GOutputStream for writing to a
+ * Windows file handle.
+ *
+ * Note that <filename>&lt;gio/gwin32outputstream.h&gt;</filename> belongs
+ * to the Windows-specific GIO interfaces, thus you have to use the
+ * <filename>gio-windows-2.0.pc</filename> pkg-config file when using it.
+ */
+
+enum {
+  PROP_0,
+  PROP_HANDLE,
+  PROP_CLOSE_HANDLE
+};
+
+G_DEFINE_TYPE (GWin32OutputStream, g_win32_output_stream, G_TYPE_OUTPUT_STREAM);
+
+
+struct _GWin32OutputStreamPrivate {
+  HANDLE handle;
+  gboolean close_handle;
+};
+
+static void     g_win32_output_stream_set_property (GObject              *object,
+						    guint                 prop_id,
+						    const GValue         *value,
+						    GParamSpec           *pspec);
+static void     g_win32_output_stream_get_property (GObject              *object,
+						    guint                 prop_id,
+						    GValue               *value,
+						    GParamSpec           *pspec);
+static gssize   g_win32_output_stream_write        (GOutputStream        *stream,
+						    const void           *buffer,
+						    gsize                 count,
+						    GCancellable         *cancellable,
+						    GError              **error);
+static gboolean g_win32_output_stream_close        (GOutputStream        *stream,
+						    GCancellable         *cancellable,
+						    GError              **error);
+
+
+static void
+g_win32_output_stream_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (g_win32_output_stream_parent_class)->finalize (object);
+}
+
+static void
+g_win32_output_stream_class_init (GWin32OutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GWin32OutputStreamPrivate));
+
+  gobject_class->get_property = g_win32_output_stream_get_property;
+  gobject_class->set_property = g_win32_output_stream_set_property;
+  gobject_class->finalize = g_win32_output_stream_finalize;
+
+  stream_class->write_fn = g_win32_output_stream_write;
+  stream_class->close_fn = g_win32_output_stream_close;
+
+   /**
+   * GWin32OutputStream:handle:
+   *
+   * The file handle that the stream writes to.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+				   PROP_HANDLE,
+				   g_param_spec_pointer ("handle",
+							 P_("File handle"),
+							 P_("The file handle to write to"),
+							 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  /**
+   * GWin32OutputStream:close-handle:
+   *
+   * Whether to close the file handle when the stream is closed.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+				   PROP_CLOSE_HANDLE,
+				   g_param_spec_boolean ("close-handle",
+							 P_("Close file handle"),
+							 P_("Whether to close the file handle when the stream is closed"),
+							 TRUE,
+							 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+}
+
+static void
+g_win32_output_stream_set_property (GObject         *object,
+				    guint            prop_id,
+				    const GValue    *value,
+				    GParamSpec      *pspec)
+{
+  GWin32OutputStream *win32_stream;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      win32_stream->priv->handle = g_value_get_pointer (value);
+      break;
+    case PROP_CLOSE_HANDLE:
+      win32_stream->priv->close_handle = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_win32_output_stream_get_property (GObject    *object,
+				    guint       prop_id,
+				    GValue     *value,
+				    GParamSpec *pspec)
+{
+  GWin32OutputStream *win32_stream;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      g_value_set_pointer (value, win32_stream->priv->handle);
+      break;
+    case PROP_CLOSE_HANDLE:
+      g_value_set_boolean (value, win32_stream->priv->close_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_win32_output_stream_init (GWin32OutputStream *win32_stream)
+{
+  win32_stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (win32_stream,
+						    G_TYPE_WIN32_OUTPUT_STREAM,
+						    GWin32OutputStreamPrivate);
+
+  win32_stream->priv->handle = NULL;
+  win32_stream->priv->close_handle = TRUE;
+}
+
+/**
+ * g_win32_output_stream_new:
+ * @handle: a Win32 file handle
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Creates a new #GWin32OutputStream for the given @handle.
+ *
+ * If @close_handle, is %TRUE, the handle will be closed when the
+ * output stream is destroyed.
+ *
+ * Returns: a new #GOutputStream
+ *
+ * Since: 2.26
+**/
+GOutputStream *
+g_win32_output_stream_new (void    *handle,
+			   gboolean close_handle)
+{
+  GWin32OutputStream *stream;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  stream = g_object_new (G_TYPE_WIN32_OUTPUT_STREAM,
+			 "handle", handle,
+			 "close-handle", close_handle,
+			 NULL);
+
+  return G_OUTPUT_STREAM (stream);
+}
+
+/**
+ * g_win32_output_stream_set_close_handle:
+ * @stream: a #GWin32OutputStream
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Sets whether the handle of @stream shall be closed when the stream
+ * is closed.
+ *
+ * Since: 2.26
+ */
+void
+g_win32_output_stream_set_close_handle (GWin32OutputStream *stream,
+					gboolean           close_handle)
+{
+  g_return_if_fail (G_IS_WIN32_OUTPUT_STREAM (stream));
+
+  close_handle = close_handle != FALSE;
+  if (stream->priv->close_handle != close_handle)
+    {
+      stream->priv->close_handle = close_handle;
+      g_object_notify (G_OBJECT (stream), "close-handle");
+    }
+}
+
+/**
+ * g_win32_output_stream_get_close_handle:
+ * @stream: a #GWin32OutputStream
+ *
+ * Returns whether the handle of @stream will be closed when the
+ * stream is closed.
+ *
+ * Return value: %TRUE if the handle is closed when done
+ *
+ * Since: 2.26
+ */
+gboolean
+g_win32_output_stream_get_close_handle (GWin32OutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_OUTPUT_STREAM (stream), FALSE);
+
+  return stream->priv->close_handle;
+}
+
+/**
+ * g_win32_output_stream_get_handle:
+ * @stream: a #GWin32OutputStream
+ *
+ * Return the Windows handle that the stream writes to.
+ *
+ * Return value: The handle descriptor of @stream
+ *
+ * Since: 2.26
+ */
+void *
+g_win32_output_stream_get_handle (GWin32OutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_OUTPUT_STREAM (stream), NULL);
+
+  return stream->priv->handle;
+}
+
+static gssize
+g_win32_output_stream_write (GOutputStream  *stream,
+			    const void     *buffer,
+			    gsize           count,
+			    GCancellable   *cancellable,
+			    GError        **error)
+{
+  GWin32OutputStream *win32_stream;
+  BOOL res;
+  DWORD nbytes, nwritten;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  res = WriteFile (win32_stream->priv->handle, buffer, nbytes, &nwritten, NULL);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      if (errsv == ERROR_HANDLE_EOF)
+	return 0;
+
+      g_set_error (error, G_IO_ERROR,
+		   g_io_error_from_win32_error (errsv),
+		   _("Error writing to handle: %s"),
+		   emsg);
+      g_free (emsg);
+      return -1;
+    }
+
+  return nwritten;
+}
+
+static gboolean
+g_win32_output_stream_close (GOutputStream  *stream,
+			     GCancellable   *cancellable,
+			     GError        **error)
+{
+  GWin32OutputStream *win32_stream;
+  BOOL res;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (stream);
+
+  if (!win32_stream->priv->close_handle)
+    return TRUE;
+
+  res = CloseHandle (win32_stream->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;
+}
+
+#define __G_WIN32_OUTPUT_STREAM_C__
+#include "gioaliasdef.c"
diff --git a/gio/gwin32outputstream.h b/gio/gwin32outputstream.h
new file mode 100644
index 0000000..399a53a
--- /dev/null
+++ b/gio/gwin32outputstream.h
@@ -0,0 +1,78 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ * Author: Tor Lillqvist <tml iki fi>
+ */
+
+#ifndef __G_WIN32_OUTPUT_STREAM_H__
+#define __G_WIN32_OUTPUT_STREAM_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_OUTPUT_STREAM         (g_win32_output_stream_get_type ())
+#define G_WIN32_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_OUTPUT_STREAM, GWin32OutputStream))
+#define G_WIN32_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_OUTPUT_STREAM, GWin32OutputStreamClass))
+#define G_IS_WIN32_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_OUTPUT_STREAM))
+#define G_IS_WIN32_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_OUTPUT_STREAM))
+#define G_WIN32_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_OUTPUT_STREAM, GWin32OutputStreamClass))
+
+/**
+ * GWin32OutputStream:
+ *
+ * Implements #GOutputStream for outputting to Windows file handles
+ **/
+typedef struct _GWin32OutputStream         GWin32OutputStream;
+typedef struct _GWin32OutputStreamClass    GWin32OutputStreamClass;
+typedef struct _GWin32OutputStreamPrivate  GWin32OutputStreamPrivate;
+
+struct _GWin32OutputStream
+{
+  GOutputStream parent_instance;
+
+  /*< private >*/
+  GWin32OutputStreamPrivate *priv;
+};
+
+struct _GWin32OutputStreamClass
+{
+  GOutputStreamClass parent_class;
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType           g_win32_output_stream_get_type         (void) G_GNUC_CONST;
+
+GOutputStream * g_win32_output_stream_new              (void               *handle,
+							gboolean            close_handle);
+void            g_win32_output_stream_set_close_handle (GWin32OutputStream *stream,
+							gboolean           close_handle);
+gboolean        g_win32_output_stream_get_close_handle (GWin32OutputStream *stream);
+void           *g_win32_output_stream_get_handle       (GWin32OutputStream *stream);
+G_END_DECLS
+
+#endif /* __G_WIN32_OUTPUT_STREAM_H__ */
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 523465e..ba3410d 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -47,6 +47,10 @@ if OS_UNIX
 TEST_PROGS += live-g-file desktop-app-info unix-fd #unix-streams
 endif
 
+if OS_WIN32
+TEST_PROGS += win32-streams
+endif
+
 memory_input_stream_SOURCES	  = memory-input-stream.c
 memory_input_stream_LDADD	  = $(progs_ldadd)
 
@@ -90,6 +94,10 @@ unix_streams_SOURCES	  = unix-streams.c
 unix_streams_LDADD	  = $(progs_ldadd) \
 	$(top_builddir)/gthread/libgthread-2.0.la
 
+win32_streams_SOURCES	  = win32-streams.c
+win32_streams_LDADD	  = $(progs_ldadd) \
+	$(top_builddir)/gthread/libgthread-2.0.la
+
 unix_fd_SOURCES	  = unix-fd.c
 unix_fd_LDADD	  = $(progs_ldadd)
 
diff --git a/gio/tests/win32-streams.c b/gio/tests/win32-streams.c
new file mode 100644
index 0000000..4ac04d4
--- /dev/null
+++ b/gio/tests/win32-streams.c
@@ -0,0 +1,292 @@
+/* GLib testing framework examples and tests
+ * Copyright (C) 2008 Red Hat, Inc
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <glib/glib.h>
+#include <gio/gio.h>
+#include <gio/gwin32inputstream.h>
+#include <gio/gwin32outputstream.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <io.h>
+
+#include <windows.h>
+
+#define DATA "abcdefghijklmnopqrstuvwxyz"
+
+int writer_pipe[2], reader_pipe[2];
+GCancellable *writer_cancel, *reader_cancel, *main_cancel;
+GMainLoop *loop;
+
+static gpointer
+writer_thread (gpointer user_data)
+{
+  GOutputStream *out;
+  gssize nwrote, offset;
+  GError *err = NULL;
+  HANDLE out_handle;
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+			     (HANDLE) (gintptr) _get_osfhandle (writer_pipe[1]),
+			     GetCurrentProcess (),
+			     &out_handle,
+			     0, FALSE,
+			     DUPLICATE_SAME_ACCESS));
+  close (writer_pipe[1]);
+
+  out = g_win32_output_stream_new (out_handle, TRUE);
+  do
+    {
+      g_usleep (10);
+
+      offset = 0;
+      while (offset < (gssize) sizeof (DATA))
+	{
+	  nwrote = g_output_stream_write (out, DATA + offset,
+					  sizeof (DATA) - offset,
+					  writer_cancel, &err);
+	  if (nwrote <= 0 || err != NULL)
+	    break;
+	  offset += nwrote;
+	}
+
+      g_assert (nwrote > 0 || err != NULL);
+    }
+  while (err == NULL);
+
+  if (g_cancellable_is_cancelled (writer_cancel))
+    {
+      g_cancellable_cancel (main_cancel);
+      g_object_unref (out);
+      return NULL;
+    }
+
+  g_warning ("writer: %s", err->message);
+  g_assert_not_reached ();
+}
+
+static gpointer
+reader_thread (gpointer user_data)
+{
+  GInputStream *in;
+  gssize nread = 0, total;
+  GError *err = NULL;
+  char buf[sizeof (DATA)];
+  HANDLE in_handle;
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+			     (HANDLE) (gintptr) _get_osfhandle (reader_pipe[0]),
+			     GetCurrentProcess (),
+			     &in_handle,
+			     0, FALSE,
+			     DUPLICATE_SAME_ACCESS));
+  close (reader_pipe[0]);
+
+  in = g_win32_input_stream_new (in_handle, TRUE);
+
+  do
+    {
+      total = 0;
+      while (total < (gssize) sizeof (DATA))
+	{
+	  nread = g_input_stream_read (in, buf + total, sizeof (buf) - total,
+				       reader_cancel, &err);
+	  if (nread <= 0 || err != NULL)
+	    break;
+	  total += nread;
+	}
+
+      if (err)
+	break;
+
+      if (nread == 0)
+	{
+	  g_assert (err == NULL);
+	  /* pipe closed */
+	  g_object_unref (in);
+	  return NULL;
+	}
+
+      g_assert_cmpstr (buf, ==, DATA);
+      g_assert (!g_cancellable_is_cancelled (reader_cancel));
+    }
+  while (err == NULL);
+
+  g_warning ("reader: %s", err->message);
+  g_assert_not_reached ();
+}
+
+char main_buf[sizeof (DATA)];
+gssize main_len, main_offset;
+
+static void readable (GObject *source, GAsyncResult *res, gpointer user_data);
+static void writable (GObject *source, GAsyncResult *res, gpointer user_data);
+
+static void
+do_main_cancel (GOutputStream *out)
+{
+  g_output_stream_close (out, NULL, NULL);
+  g_main_loop_quit (loop);
+}
+
+static void
+readable (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+  GInputStream *in = G_INPUT_STREAM (source);
+  GOutputStream *out = user_data;
+  GError *err = NULL;
+
+  main_len = g_input_stream_read_finish (in, res, &err);
+
+  if (g_cancellable_is_cancelled (main_cancel))
+    {
+      do_main_cancel (out);
+      return;
+    }
+
+  g_assert (err == NULL);
+
+  main_offset = 0;
+  g_output_stream_write_async (out, main_buf, main_len,
+			       G_PRIORITY_DEFAULT, main_cancel,
+			       writable, in);
+}
+
+static void
+writable (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+  GOutputStream *out = G_OUTPUT_STREAM (source);
+  GInputStream *in = user_data;
+  GError *err = NULL;
+  gssize nwrote;
+
+  nwrote = g_output_stream_write_finish (out, res, &err);
+
+  if (g_cancellable_is_cancelled (main_cancel))
+    {
+      do_main_cancel (out);
+      return;
+    }
+
+  g_assert (err == NULL);
+  g_assert_cmpint (nwrote, <=, main_len - main_offset);
+
+  main_offset += nwrote;
+  if (main_offset == main_len)
+    {
+      g_input_stream_read_async (in, main_buf, sizeof (main_buf),
+				 G_PRIORITY_DEFAULT, main_cancel,
+				 readable, out);
+    }
+  else
+    {
+      g_output_stream_write_async (out, main_buf + main_offset,
+				   main_len - main_offset,
+				   G_PRIORITY_DEFAULT, main_cancel,
+				   writable, in);
+    }
+}
+
+static gboolean
+timeout (gpointer cancellable)
+{
+  g_cancellable_cancel (cancellable);
+  return FALSE;
+}
+
+static void
+test_pipe_io (void)
+{
+  GThread *writer, *reader;
+  GInputStream *in;
+  GOutputStream *out;
+  HANDLE in_handle, out_handle;
+
+  /* Split off two (additional) threads, a reader and a writer. From
+   * the writer thread, write data synchronously in small chunks,
+   * which gets read asynchronously by the main thread and then
+   * written asynchronously to the reader thread, which reads it
+   * synchronously. Eventually a timeout in the main thread will cause
+   * it to cancel the writer thread, which will in turn cancel the
+   * read op in the main thread, which will then close the pipe to
+   * the reader thread, causing the read op to fail.
+   */
+
+  g_assert (_pipe (writer_pipe, 10, _O_BINARY) == 0 && _pipe (reader_pipe, 10, _O_BINARY) == 0);
+
+  writer_cancel = g_cancellable_new ();
+  reader_cancel = g_cancellable_new ();
+  main_cancel = g_cancellable_new ();
+
+  writer = g_thread_create (writer_thread, NULL, TRUE, NULL);
+  reader = g_thread_create (reader_thread, NULL, TRUE, NULL);
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+			     (HANDLE) (gintptr) _get_osfhandle (writer_pipe[0]),
+			     GetCurrentProcess (),
+			     &in_handle,
+			     0, FALSE,
+			     DUPLICATE_SAME_ACCESS));
+  close (writer_pipe[0]);
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+			     (HANDLE) (gintptr) _get_osfhandle (reader_pipe[1]),
+			     GetCurrentProcess (),
+			     &out_handle,
+			     0, FALSE,
+			     DUPLICATE_SAME_ACCESS));
+  close (reader_pipe[1]);
+
+  in = g_win32_input_stream_new (in_handle, TRUE);
+  out = g_win32_output_stream_new (out_handle, TRUE);
+
+  g_input_stream_read_async (in, main_buf, sizeof (main_buf),
+			     G_PRIORITY_DEFAULT, main_cancel,
+			     readable, out);
+
+  g_timeout_add (500, timeout, writer_cancel);
+
+  loop = g_main_loop_new (NULL, TRUE);
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+
+  g_thread_join (reader);
+  g_thread_join (writer);
+
+  g_object_unref (main_cancel);
+  g_object_unref (reader_cancel);
+  g_object_unref (writer_cancel);
+  g_object_unref (in);
+  g_object_unref (out);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_thread_init (NULL);
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/win32-streams/pipe-io-test", test_pipe_io);
+
+  return g_test_run();
+}
diff --git a/glib-zip.in b/glib-zip.in
index 859a06a..b8ceb09 100755
--- a/glib-zip.in
+++ b/glib-zip.in
@@ -64,6 +64,7 @@ lib/pkgconfig/gmodule-no-export-2.0.pc
 lib/pkgconfig/gobject-2.0.pc
 lib/pkgconfig/gthread-2.0.pc
 lib/pkgconfig/gio-2.0.pc
+lib/pkgconfig/gio-windows-2.0.pc
 share/aclocal/glib-2.0.m4
 share/aclocal/glib-gettext.m4
 share/glib-2.0



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