Skip to content

Commit f69e74b

Browse files
authored
Merge pull request #781 from tristanlabelle/PIPE_NOWAIT
Use PIPE_NOWAIT on Windows to avoid blocking on writes
2 parents 6242e80 + 4a73a75 commit f69e74b

File tree

1 file changed

+28
-21
lines changed

1 file changed

+28
-21
lines changed

src/io.c

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,9 +1432,30 @@ _dispatch_fd_entry_create_with_fd(dispatch_fd_t fd, uintptr_t hash)
14321432
#if defined(_WIN32)
14331433
DWORD dwType = GetFileType((HANDLE)fd);
14341434
if (dwType == FILE_TYPE_PIPE) {
1435-
unsigned long value = 1;
1436-
int result = ioctlsocket((SOCKET)fd, (long)FIONBIO, &value);
1437-
(void)dispatch_assume_zero(result);
1435+
if (_dispatch_handle_is_socket((HANDLE)fd)) {
1436+
unsigned long value = 1;
1437+
int result = ioctlsocket((SOCKET)fd, (long)FIONBIO, &value);
1438+
(void)dispatch_assume_zero(result);
1439+
} else {
1440+
// Try to make writing nonblocking, although pipes not coming
1441+
// from Foundation.Pipe may not have FILE_WRITE_ATTRIBUTES.
1442+
DWORD dwPipeMode = 0;
1443+
if (GetNamedPipeHandleState((HANDLE)fd, &dwPipeMode, NULL,
1444+
NULL, NULL, NULL, 0) && !(dwPipeMode & PIPE_NOWAIT)) {
1445+
dwPipeMode |= PIPE_NOWAIT;
1446+
if (!SetNamedPipeHandleState((HANDLE)fd, &dwPipeMode,
1447+
NULL, NULL)) {
1448+
// We may end up blocking on subsequent writes, but we
1449+
// don't have a good alternative.
1450+
// The WriteQuotaAvailable from NtQueryInformationFile
1451+
// erroneously returns 0 when there is a blocking read
1452+
// on the other end of the pipe.
1453+
_dispatch_fd_entry_debug("failed to set PIPE_NOWAIT",
1454+
fd_entry);
1455+
}
1456+
}
1457+
}
1458+
14381459
_dispatch_stream_init(fd_entry,
14391460
_dispatch_get_default_queue(false));
14401461
} else {
@@ -2489,24 +2510,6 @@ _dispatch_operation_perform(dispatch_operation_t op)
24892510
}
24902511
bSuccess = TRUE;
24912512
} else if (GetFileType(hFile) == FILE_TYPE_PIPE) {
2492-
// Unfortunately there isn't a good way to achieve O_NONBLOCK
2493-
// semantics when writing to a pipe. SetNamedPipeHandleState()
2494-
// can allow pipes to be switched into a "no wait" mode, but
2495-
// that doesn't work on most pipe handles because Windows
2496-
// doesn't consistently create pipes with FILE_WRITE_ATTRIBUTES
2497-
// access. The best we can do is to try to query the write quota
2498-
// and then write as much as we can.
2499-
IO_STATUS_BLOCK iosb;
2500-
FILE_PIPE_LOCAL_INFORMATION fpli;
2501-
NTSTATUS status = _dispatch_NtQueryInformationFile(hFile, &iosb,
2502-
&fpli, sizeof(fpli), FilePipeLocalInformation);
2503-
if (NT_SUCCESS(status)) {
2504-
if (fpli.WriteQuotaAvailable == 0) {
2505-
err = EAGAIN;
2506-
goto error;
2507-
}
2508-
len = MIN(len, fpli.WriteQuotaAvailable);
2509-
}
25102513
OVERLAPPED ovlOverlapped = {};
25112514
bSuccess = WriteFile(hFile, buf, (DWORD)len,
25122515
(LPDWORD)&processed, &ovlOverlapped);
@@ -2523,6 +2526,10 @@ _dispatch_operation_perform(dispatch_operation_t op)
25232526
processed = 0;
25242527
}
25252528
}
2529+
if (bSuccess && processed == 0) {
2530+
err = EAGAIN;
2531+
goto error;
2532+
}
25262533
} else {
25272534
bSuccess = WriteFile(hFile, buf, (DWORD)len,
25282535
(LPDWORD)&processed, NULL);

0 commit comments

Comments
 (0)