Skip to content

Use PIPE_NOWAIT on Windows to avoid blocking on writes #781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 8, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 28 additions & 21 deletions src/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1432,9 +1432,30 @@ _dispatch_fd_entry_create_with_fd(dispatch_fd_t fd, uintptr_t hash)
#if defined(_WIN32)
DWORD dwType = GetFileType((HANDLE)fd);
if (dwType == FILE_TYPE_PIPE) {
unsigned long value = 1;
int result = ioctlsocket((SOCKET)fd, (long)FIONBIO, &value);
(void)dispatch_assume_zero(result);
if (_dispatch_handle_is_socket((HANDLE)fd)) {
unsigned long value = 1;
int result = ioctlsocket((SOCKET)fd, (long)FIONBIO, &value);
(void)dispatch_assume_zero(result);
} else {
// Try to make writing nonblocking, although pipes not coming
// from Foundation.Pipe may not have FILE_WRITE_ATTRIBUTES.
DWORD dwPipeMode = 0;
if (GetNamedPipeHandleState((HANDLE)fd, &dwPipeMode, NULL,
NULL, NULL, NULL, 0) && !(dwPipeMode & PIPE_NOWAIT)) {
dwPipeMode |= PIPE_NOWAIT;
if (!SetNamedPipeHandleState((HANDLE)fd, &dwPipeMode,
NULL, NULL)) {
// We may end up blocking on subsequent writes, but we
// don't have a good alternative.
// The WriteQuotaAvailable from NtQueryInformationFile
// erroneously returns 0 when there is a blocking read
// on the other end of the pipe.
_dispatch_fd_entry_debug("failed to set PIPE_NOWAIT",
fd_entry);
}
}
}

_dispatch_stream_init(fd_entry,
_dispatch_get_default_queue(false));
} else {
Expand Down Expand Up @@ -2489,24 +2510,6 @@ _dispatch_operation_perform(dispatch_operation_t op)
}
bSuccess = TRUE;
} else if (GetFileType(hFile) == FILE_TYPE_PIPE) {
// Unfortunately there isn't a good way to achieve O_NONBLOCK
// semantics when writing to a pipe. SetNamedPipeHandleState()
// can allow pipes to be switched into a "no wait" mode, but
// that doesn't work on most pipe handles because Windows
// doesn't consistently create pipes with FILE_WRITE_ATTRIBUTES
// access. The best we can do is to try to query the write quota
// and then write as much as we can.
IO_STATUS_BLOCK iosb;
FILE_PIPE_LOCAL_INFORMATION fpli;
NTSTATUS status = _dispatch_NtQueryInformationFile(hFile, &iosb,
&fpli, sizeof(fpli), FilePipeLocalInformation);
if (NT_SUCCESS(status)) {
if (fpli.WriteQuotaAvailable == 0) {
err = EAGAIN;
goto error;
}
len = MIN(len, fpli.WriteQuotaAvailable);
}
OVERLAPPED ovlOverlapped = {};
bSuccess = WriteFile(hFile, buf, (DWORD)len,
(LPDWORD)&processed, &ovlOverlapped);
Expand All @@ -2523,6 +2526,10 @@ _dispatch_operation_perform(dispatch_operation_t op)
processed = 0;
}
}
if (bSuccess && processed == 0) {
err = EAGAIN;
goto error;
}
} else {
bSuccess = WriteFile(hFile, buf, (DWORD)len,
(LPDWORD)&processed, NULL);
Expand Down