[newlib-cygwin] Cygwin: pipe: Adopt FILE_SYNCHRONOUS_IO_NONALERT flag for read pipe.

Takashi Yano tyan0@sourceware.org
Mon Dec 13 10:40:01 GMT 2021


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=9e4d308cd592fe383dec58ea6523c1b436888ef8

commit 9e4d308cd592fe383dec58ea6523c1b436888ef8
Author: Takashi Yano <takashi.yano@nifty.ne.jp>
Date:   Wed Nov 10 21:47:09 2021 +0900

    Cygwin: pipe: Adopt FILE_SYNCHRONOUS_IO_NONALERT flag for read pipe.
    
    - With this patch, FILE_SYNCHRONOUS_IO_NONALERT flag is applied to
      the read pipe so that native C# programs work with cygwin pipe.
      To handle signals in reading pipe with this flag set, the read
      pipe itself is always set to nonblocking mode and simulates the
      blocking behaviour in raw_read().
    
    - Default to byte mode rather than message mode in order to make
      C# programs work with cygwin pipe.
    
    Addresses: https://cygwin.com/pipermail/cygwin/2021-March/247987.html

Diff:
---
 winsup/cygwin/fhandler_pipe.cc | 72 ++++++++++++++++--------------------------
 winsup/cygwin/globals.cc       |  2 +-
 2 files changed, 28 insertions(+), 46 deletions(-)

diff --git a/winsup/cygwin/fhandler_pipe.cc b/winsup/cygwin/fhandler_pipe.cc
index 5195b2807..40fb2349a 100644
--- a/winsup/cygwin/fhandler_pipe.cc
+++ b/winsup/cygwin/fhandler_pipe.cc
@@ -90,7 +90,10 @@ fhandler_pipe::init (HANDLE f, DWORD a, mode_t mode, int64_t uniq_id)
   set_ino (uniq_id);
   set_unique_id (uniq_id | !!(mode & GENERIC_WRITE));
   if (opened_properly)
-    set_pipe_non_blocking (is_nonblocking ());
+    /* Set read pipe always nonblocking to allow signal handling
+       even with FILE_SYNCHRONOUS_IO_NONALERT. */
+    set_pipe_non_blocking (get_device () == FH_PIPER ?
+			   true : is_nonblocking ());
   return 1;
 }
 
@@ -264,9 +267,9 @@ fhandler_pipe::release_select_sem (const char *from)
   if (get_dev () == FH_PIPER) /* Number of select() and writer */
     n_release = get_obj_handle_count (select_sem)
       - get_obj_handle_count (read_mtx);
-  else /* Number of select() call */
+  else /* Number of select() call and reader */
     n_release = get_obj_handle_count (select_sem)
-      - get_obj_handle_count (hdl_cnt_mtx);
+      - get_obj_handle_count (get_handle ());
   debug_printf("%s(%s) release %d", from,
 	       get_dev () == FH_PIPER ? "PIPER" : "PIPEW", n_release);
   if (n_release)
@@ -279,7 +282,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
   size_t nbytes = 0;
   NTSTATUS status = STATUS_SUCCESS;
   IO_STATUS_BLOCK io;
-  HANDLE evt;
 
   if (!len)
     return;
@@ -307,60 +309,27 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
       len = (size_t) -1;
       return;
     }
-
-  if (!(evt = CreateEvent (NULL, false, false, NULL)))
-    {
-      __seterrno ();
-      len = (size_t) -1;
-      ReleaseMutex (read_mtx);
-      return;
-    }
-
   while (nbytes < len)
     {
       ULONG_PTR nbytes_now = 0;
       ULONG len1 = (ULONG) (len - nbytes);
-      waitret = WAIT_OBJECT_0;
 
-      ResetEvent (evt);
       FILE_PIPE_LOCAL_INFORMATION fpli;
       status = NtQueryInformationFile (get_handle (), &io,
 				       &fpli, sizeof (fpli),
 				       FilePipeLocalInformation);
       if (NT_SUCCESS (status))
 	{
-	if (fpli.ReadDataAvailable == 0 && nbytes != 0)
-	  break;
+	  if (fpli.ReadDataAvailable == 0 && nbytes != 0)
+	    break;
 	}
       else if (nbytes != 0)
 	break;
-      status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr,
+      status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, ptr,
 			   len1, NULL, NULL);
-      if (status == STATUS_PENDING)
-	{
-	  waitret = cygwait (evt, INFINITE, cw_cancel | cw_sig);
-	  /* 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 (isclosed ())  /* A signal handler might have closed the fd. */
 	{
-	  if (waitret == WAIT_OBJECT_0)
-	    set_errno (EBADF);
-	  else
-	    __seterrno ();
+	  set_errno (EBADF);
 	  nbytes = (size_t) -1;
 	}
       else if (NT_SUCCESS (status)
@@ -393,7 +362,16 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
 		  nbytes = (size_t) -1;
 		  break;
 		}
-	      fallthrough;
+	      waitret = cygwait (select_sem, 1);
+	      if (waitret == WAIT_CANCELED)
+		pthread::static_cancel_self ();
+	      else if (waitret == WAIT_SIGNALED)
+		{
+		  set_errno (EINTR);
+		  nbytes = (size_t) -1;
+		  break;
+		}
+	      continue;
 	    default:
 	      __seterrno_from_nt_status (status);
 	      nbytes = (size_t) -1;
@@ -406,7 +384,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
 	break;
     }
   ReleaseMutex (read_mtx);
-  CloseHandle (evt);
   if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
     {
       set_errno (EINTR);
@@ -990,9 +967,12 @@ nt_create (LPSECURITY_ATTRIBUTES sa_ptr, HANDLE &r, HANDLE &w,
 				  npfsh, sa_ptr->lpSecurityDescriptor);
 
       timeout.QuadPart = -500000;
+      /* Set FILE_SYNCHRONOUS_IO_NONALERT flag so that native
+	 C# programs work with cygwin pipe. */
       status = NtCreateNamedPipeFile (&r, access, &attr, &io,
 				      FILE_SHARE_READ | FILE_SHARE_WRITE,
-				      FILE_CREATE, 0, pipe_type,
+				      FILE_CREATE,
+				      FILE_SYNCHRONOUS_IO_NONALERT, pipe_type,
 				      FILE_PIPE_BYTE_STREAM_MODE,
 				      0, 1, psize, psize, &timeout);
 
@@ -1104,7 +1084,9 @@ fhandler_pipe::fcntl (int cmd, intptr_t arg)
   const bool was_nonblocking = is_nonblocking ();
   int res = fhandler_base::fcntl (cmd, arg);
   const bool now_nonblocking = is_nonblocking ();
-  if (now_nonblocking != was_nonblocking)
+  /* Do not set blocking mode for read pipe to allow signal handling
+     even with FILE_SYNCHRONOUS_IO_NONALERT. */
+  if (now_nonblocking != was_nonblocking && get_device () != FH_PIPER)
     set_pipe_non_blocking (now_nonblocking);
   return res;
 }
diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc
index 48fb312de..ac5ad0307 100644
--- a/winsup/cygwin/globals.cc
+++ b/winsup/cygwin/globals.cc
@@ -69,7 +69,7 @@ int NO_COPY dynamically_loaded;
 /* Some CYGWIN environment variable variables. */
 bool allow_glob = true;
 bool ignore_case_with_glob;
-bool pipe_byte;
+bool pipe_byte = true; /* Default to byte mode so that C# programs work. */
 bool reset_com;
 bool wincmdln;
 winsym_t allow_winsymlinks = WSYM_default;


More information about the Cygwin-cvs mailing list