@@ -1462,20 +1462,20 @@ _dispatch_fd_entry_create_with_fd(dispatch_fd_t fd, uintptr_t hash)
1462
1462
int result = ioctlsocket ((SOCKET )fd , (long )FIONBIO , & value );
1463
1463
(void )dispatch_assume_zero (result );
1464
1464
} else {
1465
- // The _dispatch_pipe_monitor_thread expects pipes to be
1466
- // PIPE_WAIT and exploits this assumption by using a blocking
1467
- // 0-byte read as a synchronization mechanism.
1465
+ // Try to make writing nonblocking, although pipes not coming
1466
+ // from Foundation.Pipe may not have FILE_WRITE_ATTRIBUTES.
1468
1467
DWORD dwPipeMode = 0 ;
1469
1468
if (GetNamedPipeHandleState ((HANDLE )fd , & dwPipeMode , NULL ,
1470
- NULL , NULL , NULL , 0 ) && !(dwPipeMode & PIPE_WAIT )) {
1471
- dwPipeMode |= PIPE_WAIT ;
1469
+ NULL , NULL , NULL , 0 ) && !(dwPipeMode & PIPE_NOWAIT )) {
1470
+ dwPipeMode |= PIPE_NOWAIT ;
1472
1471
if (!SetNamedPipeHandleState ((HANDLE )fd , & dwPipeMode ,
1473
1472
NULL , NULL )) {
1474
- // If setting the pipe to PIPE_WAIT fails, the
1475
- // monitoring thread will spin constantly, saturating
1476
- // a core, which is undesirable but non-fatal.
1477
- // The semantics will still be correct in this case.
1478
- _dispatch_fd_entry_debug ("failed to set PIPE_WAIT" ,
1473
+ // We may end up blocking on subsequent writes, but we
1474
+ // don't have a good alternative.
1475
+ // The WriteQuotaAvailable from NtQueryInformationFile
1476
+ // erroneously returns 0 when there is a blocking read
1477
+ // on the other end of the pipe.
1478
+ _dispatch_fd_entry_debug ("failed to set PIPE_NOWAIT" ,
1479
1479
fd_entry );
1480
1480
}
1481
1481
}
@@ -2550,40 +2550,13 @@ _dispatch_operation_perform(dispatch_operation_t op)
2550
2550
NTSTATUS status = _dispatch_NtQueryInformationFile (hFile ,
2551
2551
& iosb , & fpli , sizeof (fpli ), FilePipeLocalInformation );
2552
2552
if (NT_SUCCESS (status )) {
2553
- // WriteQuotaAvailable is the free space in the output buffer
2554
- // that has not already been reserved for reading. In other words,
2555
- // WriteQuotaAvailable =
2556
- // OutboundQuota - WriteQuotaUsed - QueuedReadSize.
2557
- // It is not documented that QueuedReadSize is part of this
2558
- // calculation, but this behavior has been observed experimentally.
2559
- // Unfortunately, this means that it is not possible to distinguish
2560
- // between a full output buffer and a reader blocked waiting for a
2561
- // full buffer's worth of data. This is a problem because if the
2562
- // output buffer is full and no reader is waiting for data, then
2563
- // attempting to write to the buffer of a PIPE_WAIT, non-
2564
- // overlapped I/O pipe will block the dispatch queue thread.
2565
- //
2566
- // In order to work around this idiosyncrasy, we bound the size of
2567
- // the write to be OutboundQuota - 1. This affords us a sentinel value
2568
- // in WriteQuotaAvailable that can be used to detect if a reader is
2569
- // making progress or not.
2570
- // WriteQuotaAvailable = 0 => a reader is blocked waiting for data.
2571
- // WriteQuotaAvailable = 1 => the pipe has been written to, but no
2572
- // reader is making progress.
2573
- // When we detect that WriteQuotaAvailable == 1, we write 0 bytes to
2574
- // avoid blocking the dispatch queue thread.
2575
- if (fpli .WriteQuotaAvailable == 0 ) {
2576
- // This condition can only occur when we have a reader blocked
2577
- // waiting for data on the pipe. In this case, write a full
2578
- // buffer's worth of data (less one byte to preserve this
2579
- // sentinel value of WriteQuotaAvailable == 0).
2580
- len = MIN (len , fpli .OutboundQuota - 1 );
2581
- } else {
2582
- // Subtract 1 from WriteQuotaAvailable to ensure we do not fill
2583
- // the pipe and preserve the sentinel value of
2584
- // WriteQuotaAvailable == 1.
2585
- len = MIN (len , fpli .WriteQuotaAvailable - 1 );
2553
+ // WriteQuotaAvailable is unreliable in the presence
2554
+ // of a blocking reader, when it can return zero, so only
2555
+ // account for it otherwise
2556
+ if (fpli .WriteQuotaAvailable > 0 ) {
2557
+ len = MIN (len , fpli .WriteQuotaAvailable );
2586
2558
}
2559
+ len = MIN (len , fpli .OutboundQuota );
2587
2560
}
2588
2561
2589
2562
OVERLAPPED ovlOverlapped = {};
0 commit comments