mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-26 22:16:16 +01:00
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
This commit is contained in:
parent
96a0c589ee
commit
23d80a04da
@ -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
|
||||
|
@ -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__ */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user