- 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
---
 winsup/cygwin/fhandler_pipe.cc | 81 ++++++++++++++++------------------
 winsup/cygwin/release/3.3.3    |  5 +++
 2 files changed, 43 insertions(+), 43 deletions(-)

diff --git a/winsup/cygwin/fhandler_pipe.cc b/winsup/cygwin/fhandler_pipe.cc
index 9392a28c1..3cbd434b7 100644
--- a/winsup/cygwin/fhandler_pipe.cc
+++ b/winsup/cygwin/fhandler_pipe.cc
@@ -279,13 +279,12 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
   size_t nbytes = 0;
   NTSTATUS status = STATUS_SUCCESS;
   IO_STATUS_BLOCK io;
-  HANDLE evt = NULL;
+  HANDLE evt;
 
   if (!len)
     return;
 
-  /* Create a wait event if we're in blocking mode. */
-  if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
+  if (!(evt = CreateEvent (NULL, false, false, NULL)))
     {
       __seterrno ();
       len = (size_t) -1;
@@ -321,8 +320,7 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
       ULONG len1 = (ULONG) (len - nbytes);
       waitret = WAIT_OBJECT_0;
 
-      if (evt)
-       ResetEvent (evt);
+      ResetEvent (evt);
       FILE_PIPE_LOCAL_INFORMATION fpli;
       status = NtQueryInformationFile (get_handle (), &io,
                                       &fpli, sizeof (fpli),
@@ -336,7 +334,7 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
        break;
       status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr,
                           len1, NULL, NULL);
-      if (evt && status == STATUS_PENDING)
+      if (status == STATUS_PENDING)
        {
          waitret = cygwait (evt, INFINITE, cw_cancel | cw_sig);
          /* If io.Status is STATUS_CANCELLED after CancelIo, IO has actually
@@ -406,8 +404,7 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
        break;
     }
   ReleaseMutex (read_mtx);
-  if (evt)
-    CloseHandle (evt);
+  CloseHandle (evt);
   if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
     {
       set_errno (EINTR);
@@ -437,7 +434,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
   ULONG chunk;
   NTSTATUS status = STATUS_SUCCESS;
   IO_STATUS_BLOCK io;
-  HANDLE evt = NULL;
+  HANDLE evt;
 
   if (!len)
     return 0;
@@ -456,8 +453,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
   else
     chunk = pipe_buf_size;
 
-  /* Create a wait event if the pipe or fifo is in blocking mode. */
-  if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
+  if (!(evt = CreateEvent (NULL, false, false, NULL)))
     {
       __seterrno ();
       return -1;
@@ -502,41 +498,41 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t 
len)
        {
          status = NtWriteFile (get_handle (), evt, NULL, NULL, &io,
                                (PVOID) ptr, len1, NULL, NULL);
-         if (evt || !NT_SUCCESS (status) || io.Information > 0
-             || len <= PIPE_BUF)
-           break;
-         len1 >>= 1;
-       }
-      if (evt && status == STATUS_PENDING)
-       {
-         while (WAIT_TIMEOUT ==
-                (waitret = cygwait (evt, (DWORD) 0, cw_cancel | cw_sig)))
+         if (status == STATUS_PENDING)
            {
-             if (reader_closed ())
+             while (WAIT_TIMEOUT ==
+                    (waitret = cygwait (evt, (DWORD) 0, cw_cancel | cw_sig)))
                {
-                 CancelIo (get_handle ());
-                 set_errno (EPIPE);
-                 raise (SIGPIPE);
-                 goto out;
+                 if (reader_closed ())
+                   {
+                     CancelIo (get_handle ());
+                     set_errno (EPIPE);
+                     raise (SIGPIPE);
+                     goto out;
+                   }
+                 else
+                   cygwait (select_sem, 10);
                }
+             /* If io.Status is STATUS_CANCELLED after CancelIo, IO has
+                actually been cancelled and io.Information contains the
+                number of bytes processed so far.
+                Otherwise IO has been finished regulary and io.Status
+                contains valid success or error information. */
+             CancelIo (get_handle ());
+             if (waitret == WAIT_SIGNALED && io.Status != STATUS_CANCELLED)
+               waitret = WAIT_OBJECT_0;
+
+             if (waitret == WAIT_CANCELED)
+               status = STATUS_THREAD_CANCELED;
+             else if (waitret == WAIT_SIGNALED)
+               status = STATUS_THREAD_SIGNALED;
              else
-               cygwait (select_sem, 10);
+               status = io.Status;
            }
-         /* If io.Status is STATUS_CANCELLED after CancelIo, IO has actually
-            been cancelled and io.Information contains the number of bytes
-            processed so far.
-            Otherwise IO has been finished regulary and io.Status contains
-            valid success or error information. */
-         CancelIo (get_handle ());
-         if (waitret == WAIT_SIGNALED && io.Status != STATUS_CANCELLED)
-           waitret = WAIT_OBJECT_0;
-
-         if (waitret == WAIT_CANCELED)
-           status = STATUS_THREAD_CANCELED;
-         else if (waitret == WAIT_SIGNALED)
-           status = STATUS_THREAD_SIGNALED;
-         else
-           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. */
        {
@@ -570,8 +566,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len)
        break;
     }
 out:
-  if (evt)
-    CloseHandle (evt);
+  CloseHandle (evt);
   if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
     set_errno (EINTR);
   else if (status == STATUS_THREAD_CANCELED)
diff --git a/winsup/cygwin/release/3.3.3 b/winsup/cygwin/release/3.3.3
index c1e8cefbd..615c57977 100644
--- a/winsup/cygwin/release/3.3.3
+++ b/winsup/cygwin/release/3.3.3
@@ -22,3 +22,8 @@ Bug Fixes
   running bash in Windows Terminal and inserting an emoji does not
   work as expected.
   Addresses: https://github.com/git-for-windows/git/issues/3281
+
+- Fix issue that pipe read()/write() occationally returns a garnage
+  length when NtReadFile/NtWriteFile returns STATUS_PENDING in non-
+  blocking mode.
+  Addresses: https://cygwin.com/pipermail/cygwin/2021-November/249910.html
-- 
2.33.0

Reply via email to