[PATCH v3] Cygwin: pipe: Handle STATUS_PENDING even for nonblocking mode.

Takashi Yano takashi.yano@nifty.ne.jp
Tue Nov 16 14:33:28 GMT 2021


- 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



More information about the Cygwin-patches mailing list