Cygwin: pipe: Handle STATUS_PENDING even for nonblocking mode.

- NtReadFile() and NtWriteFile() seems to return STATUS_PENDING
  occasionally even in nonblocking mode. This patch adds handling
  for STATUS_PENDING in nonblocking mode.

Addresses:
  https://cygwin.com/pipermail/cygwin/2021-November/249910.html
This commit is contained in:
Takashi Yano 2021-11-17 08:13:17 +09:00
parent 19955359c7
commit 6ac73b36c8
2 changed files with 45 additions and 45 deletions

View File

@ -279,13 +279,12 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
size_t nbytes = 0; size_t nbytes = 0;
NTSTATUS status = STATUS_SUCCESS; NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
HANDLE evt = NULL; HANDLE evt;
if (!len) if (!len)
return; return;
/* Create a wait event if we're in blocking mode. */ if (!(evt = CreateEvent (NULL, false, false, NULL)))
if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
{ {
__seterrno (); __seterrno ();
len = (size_t) -1; len = (size_t) -1;
@ -321,7 +320,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
ULONG len1 = (ULONG) (len - nbytes); ULONG len1 = (ULONG) (len - nbytes);
waitret = WAIT_OBJECT_0; waitret = WAIT_OBJECT_0;
if (evt)
ResetEvent (evt); ResetEvent (evt);
FILE_PIPE_LOCAL_INFORMATION fpli; FILE_PIPE_LOCAL_INFORMATION fpli;
status = NtQueryInformationFile (get_handle (), &io, status = NtQueryInformationFile (get_handle (), &io,
@ -336,7 +334,7 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
break; break;
status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr, status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr,
len1, NULL, NULL); len1, NULL, NULL);
if (evt && status == STATUS_PENDING) if (status == STATUS_PENDING)
{ {
waitret = cygwait (evt, INFINITE, cw_cancel | cw_sig); waitret = cygwait (evt, INFINITE, cw_cancel | cw_sig);
/* If io.Status is STATUS_CANCELLED after CancelIo, IO has actually /* If io.Status is STATUS_CANCELLED after CancelIo, IO has actually
@ -406,7 +404,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
break; break;
} }
ReleaseMutex (read_mtx); ReleaseMutex (read_mtx);
if (evt)
CloseHandle (evt); CloseHandle (evt);
if (status == STATUS_THREAD_SIGNALED && nbytes == 0) if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
{ {
@ -437,7 +434,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
ULONG chunk; ULONG chunk;
NTSTATUS status = STATUS_SUCCESS; NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
HANDLE evt = NULL; HANDLE evt;
if (!len) if (!len)
return 0; return 0;
@ -456,8 +453,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
else else
chunk = pipe_buf_size; chunk = pipe_buf_size;
/* Create a wait event if the pipe or fifo is in blocking mode. */ if (!(evt = CreateEvent (NULL, false, false, NULL)))
if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
{ {
__seterrno (); __seterrno ();
return -1; return -1;
@ -502,12 +498,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
{ {
status = NtWriteFile (get_handle (), evt, NULL, NULL, &io, status = NtWriteFile (get_handle (), evt, NULL, NULL, &io,
(PVOID) ptr, len1, NULL, NULL); (PVOID) ptr, len1, NULL, NULL);
if (evt || !NT_SUCCESS (status) || io.Information > 0 if (status == STATUS_PENDING)
|| len <= PIPE_BUF)
break;
len1 >>= 1;
}
if (evt && status == STATUS_PENDING)
{ {
while (WAIT_TIMEOUT == while (WAIT_TIMEOUT ==
(waitret = cygwait (evt, (DWORD) 0, cw_cancel | cw_sig))) (waitret = cygwait (evt, (DWORD) 0, cw_cancel | cw_sig)))
@ -522,11 +513,11 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
else else
cygwait (select_sem, 10); cygwait (select_sem, 10);
} }
/* If io.Status is STATUS_CANCELLED after CancelIo, IO has actually /* If io.Status is STATUS_CANCELLED after CancelIo, IO has
been cancelled and io.Information contains the number of bytes actually been cancelled and io.Information contains the
processed so far. number of bytes processed so far.
Otherwise IO has been finished regulary and io.Status contains Otherwise IO has been finished regulary and io.Status
valid success or error information. */ contains valid success or error information. */
CancelIo (get_handle ()); CancelIo (get_handle ());
if (waitret == WAIT_SIGNALED && io.Status != STATUS_CANCELLED) if (waitret == WAIT_SIGNALED && io.Status != STATUS_CANCELLED)
waitret = WAIT_OBJECT_0; waitret = WAIT_OBJECT_0;
@ -538,6 +529,11 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
else else
status = io.Status; status = io.Status;
} }
if (!is_nonblocking () || !NT_SUCCESS (status) || io.Information > 0
|| len <= PIPE_BUF)
break;
len1 >>= 1;
}
if (isclosed ()) /* A signal handler might have closed the fd. */ if (isclosed ()) /* A signal handler might have closed the fd. */
{ {
if (waitret == WAIT_OBJECT_0) if (waitret == WAIT_OBJECT_0)
@ -570,7 +566,6 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
break; break;
} }
out: out:
if (evt)
CloseHandle (evt); CloseHandle (evt);
if (status == STATUS_THREAD_SIGNALED && nbytes == 0) if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
set_errno (EINTR); set_errno (EINTR);

View File

@ -22,3 +22,8 @@ Bug Fixes
- Fix long-standing problem that fchmod or facl on newly created files - Fix long-standing problem that fchmod or facl on newly created files
screw up the DOS file attributes. screw up the DOS file attributes.
Addresses: https://cygwin.com/pipermail/cygwin/2021-November/249909.html Addresses: https://cygwin.com/pipermail/cygwin/2021-November/249909.html
- Fix issue that pipe read()/write() occasionally returns a garbage
length when NtReadFile/NtWriteFile returns STATUS_PENDING in non-
blocking mode.
Addresses: https://cygwin.com/pipermail/cygwin/2021-November/249910.html