diff --git a/gio/gasynchelper.c b/gio/gasynchelper.c index 34bba9534..fa6c8162b 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 cd6d28295..cbee6a669 100644 --- a/gio/gasynchelper.h +++ b/gio/gasynchelper.h @@ -25,6 +25,10 @@ #include +#ifdef G_OS_WIN32 +#include +#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 5576f0260..58b99e0a0 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_IO_PENDING && + _g_win32_overlap_wait_result (win32_stream->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_HANDLE_EOF || - errsv == ERROR_BROKEN_PIPE) - return 0; + 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 -1; + 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 609e4f423..5a6798b51 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; + if (errsv == ERROR_IO_PENDING && + _g_win32_overlap_wait_result (win32_stream->priv->handle, + &overlap, &nwritten, cancellable)) + { + retval = nwritten; + goto end; + } - 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 (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