[glib] win32: use overlapped events for streams



commit 23d80a04da43c08f4ea626283607a7546d7a56db
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date:   Mon Jul 2 21:45:41 2012 +0200

    win32: use overlapped events for streams
    
    Any file handle created with FLAG_OVERLAPPED must have
    ReadFile()/WriteFile() called with an OVERLAPPED structure.
    Failing to do so will give unspecified results, invalid read/write or
    corruption.
    
    Without FLAG_OVERLAPPED, it is not possible to read and write
    concurrently, even with two seperate threads, created by 2 input and
    output gio streams. Also, only with FLAG_OVERLAPPED may an IO
    operation be asynchronous and thus be cancellable.
    
    We may want to call ReOpenFile() to make sure the FLAG is set, but
    this API is only available since Vista+.
    
    According to MSDN doc, adding the OVERLAPPED argument for IO operation
    on handles without FLAG_OVERLAPPED is allowed, and indeed the existing
    test still passes.
    
    v2:
    - update GetLastError() after _g_win32_overlap_wait_result ()
    - split the unrelated ERROR_MORE_DATA handling
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679288

 gio/gasynchelper.c       |   28 ++++++++++++++++++++++
 gio/gasynchelper.h       |   11 ++++++++
 gio/gwin32inputstream.c  |   58 ++++++++++++++++++++++++++++++++++-----------
 gio/gwin32outputstream.c |   53 +++++++++++++++++++++++++++++++----------
 4 files changed, 123 insertions(+), 27 deletions(-)
---
diff --git a/gio/gasynchelper.c b/gio/gasynchelper.c
index 34bba95..fa6c816 100644
--- a/gio/gasynchelper.c
+++ b/gio/gasynchelper.c
@@ -168,3 +168,31 @@ _g_fd_source_new (int           fd,
 
   return source;
 }
+
+#ifdef G_OS_WIN32
+gboolean
+_g_win32_overlap_wait_result (HANDLE           hfile,
+                              OVERLAPPED      *overlap,
+                              DWORD           *transferred,
+                              GCancellable    *cancellable G_GNUC_UNUSED)
+{
+  GPollFD pollfd[1] = { 0, };
+  gboolean result = FALSE;
+  gint num, npoll;
+
+  pollfd[0].fd = (gint)overlap->hEvent;
+  pollfd[0].events = G_IO_IN;
+  num = 1;
+
+  npoll = g_poll (pollfd, num, -1);
+  if (npoll <= 0)
+    /* error out, should never happen */
+    goto end;
+
+  /* either cancelled or IO completed, either way get the result */
+  result = GetOverlappedResult (overlap->hEvent, overlap, transferred, TRUE);
+
+end:
+  return result;
+}
+#endif
diff --git a/gio/gasynchelper.h b/gio/gasynchelper.h
index cd6d282..cbee6a6 100644
--- a/gio/gasynchelper.h
+++ b/gio/gasynchelper.h
@@ -25,6 +25,10 @@
 
 #include <gio/gio.h>
 
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
 G_BEGIN_DECLS
 
 typedef gboolean (*GFDSourceFunc) (int          fd,
@@ -35,6 +39,13 @@ GSource *_g_fd_source_new      (int              fd,
 				gushort          events,
 				GCancellable    *cancellable);
 
+#ifdef G_OS_WIN32
+gboolean _g_win32_overlap_wait_result (HANDLE           hfile,
+                                       OVERLAPPED      *overlap,
+                                       DWORD           *transferred,
+                                       GCancellable    *cancellable);
+#endif
+
 G_END_DECLS
 
 #endif /* __G_ASYNC_HELPER_H__ */
diff --git a/gio/gwin32inputstream.c b/gio/gwin32inputstream.c
index 5576f02..58b99e0 100644
--- a/gio/gwin32inputstream.c
+++ b/gio/gwin32inputstream.c
@@ -291,6 +291,8 @@ g_win32_input_stream_read (GInputStream  *stream,
   GWin32InputStream *win32_stream;
   BOOL res;
   DWORD nbytes, nread;
+  OVERLAPPED overlap = { 0, };
+  gssize retval = -1;
 
   win32_stream = G_WIN32_INPUT_STREAM (stream);
 
@@ -302,26 +304,54 @@ g_win32_input_stream_read (GInputStream  *stream,
   else
     nbytes = count;
 
-  res = ReadFile (win32_stream->priv->handle, buffer, nbytes, &nread, NULL);
-  if (!res)
+  overlap.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+  g_return_val_if_fail (overlap.hEvent != NULL, -1);
+
+  res = ReadFile (win32_stream->priv->handle, buffer, nbytes, &nread, &overlap);
+  if (res)
+    retval = nread;
+  else
     {
       int errsv = GetLastError ();
-      gchar *emsg;
 
-      if (errsv == ERROR_HANDLE_EOF ||
-	  errsv == ERROR_BROKEN_PIPE)
-	return 0;
+      if (errsv == ERROR_IO_PENDING &&
+          _g_win32_overlap_wait_result (win32_stream->priv->handle,
+                                        &overlap, &nread, cancellable))
+        {
+          retval = nread;
+          goto end;
+        }
 
-      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;
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+        goto end;
+
+      errsv = GetLastError ();
+      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);
+        }
     }
 
-  return nread;
+end:
+  CloseHandle (overlap.hEvent);
+  return retval;
 }
 
 static gboolean
diff --git a/gio/gwin32outputstream.c b/gio/gwin32outputstream.c
index 609e4f4..5a6798b 100644
--- a/gio/gwin32outputstream.c
+++ b/gio/gwin32outputstream.c
@@ -293,6 +293,8 @@ g_win32_output_stream_write (GOutputStream  *stream,
   GWin32OutputStream *win32_stream;
   BOOL res;
   DWORD nbytes, nwritten;
+  OVERLAPPED overlap = { 0, };
+  gssize retval = -1;
 
   win32_stream = G_WIN32_OUTPUT_STREAM (stream);
 
@@ -304,24 +306,49 @@ g_win32_output_stream_write (GOutputStream  *stream,
   else
     nbytes = count;
 
-  res = WriteFile (win32_stream->priv->handle, buffer, nbytes, &nwritten, NULL);
-  if (!res)
+  overlap.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+  g_return_val_if_fail (overlap.hEvent != NULL, -1);
+
+  res = WriteFile (win32_stream->priv->handle, buffer, nbytes, &nwritten, &overlap);
+  if (res)
+    retval = nwritten;
+  else
     {
       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;
+      if (errsv == ERROR_IO_PENDING &&
+          _g_win32_overlap_wait_result (win32_stream->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);
+        }
     }
 
-  return nwritten;
+end:
+  CloseHandle (overlap.hEvent);
+  return retval;
 }
 
 static gboolean



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