[newlib-cygwin] Cygwin: FIFO: improve raw_write

Corinna Vinschen corinna@sourceware.org
Tue Apr 16 11:16:00 GMT 2019


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

commit bb466278713a68d68fd507cb8f2ace6142f0a58c
Author: Ken Brown <kbrown@cornell.edu>
Date:   Mon Apr 15 15:43:57 2019 +0000

    Cygwin: FIFO: improve raw_write
    
    Don't set the write end of the pipe to non-blocking mode if the FIFO
    is opened in blocking mode.
    
    In fhandler_fifo::raw_write in blocking mode, wait for the write to
    complete rather than returning -1 with EAGAIN.
    
    If the amount to write is large, write in smaller chunks (of size
    determined by a new data member max_atomic_write), as in
    fhandler_base_overlapped.
    
    For convenience, add two new NTSTATUS codes, STATUS_THREAD_SIGNALED
    and STATUS_THREAD_CANCELED, to ntdll.h.

Diff:
---
 winsup/cygwin/fhandler.h       |  1 +
 winsup/cygwin/fhandler_fifo.cc | 95 +++++++++++++++++++++++++++++++++---------
 winsup/cygwin/ntdll.h          |  4 +-
 3 files changed, 80 insertions(+), 20 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index aea02c2..fd205a6 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1273,6 +1273,7 @@ class fhandler_fifo: public fhandler_base
   int nhandlers, nconnected;
   af_unix_spinlock_t _fifo_client_lock;
   bool reader, writer, duplexer;
+  size_t max_atomic_write;
   bool __reg2 wait (HANDLE);
   NTSTATUS npfs_handle (HANDLE &);
   HANDLE create_pipe_instance (bool);
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index fe4c67b..d2f8b99 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -39,7 +39,8 @@ STATUS_PIPE_EMPTY simply means there's no data to be read. */
 fhandler_fifo::fhandler_fifo ():
   fhandler_base (), read_ready (NULL), write_ready (NULL),
   listen_client_thr (NULL), lct_termination_evt (NULL), nhandlers (0),
-  nconnected (0), reader (false), writer (false), duplexer (false)
+  nconnected (0), reader (false), writer (false), duplexer (false),
+  max_atomic_write (DEFAULT_PIPEBUFSIZE)
 {
   pipe_name_buf[0] = L'\0';
   need_fork_fixup (true);
@@ -559,7 +560,7 @@ fhandler_fifo::open (int flags, mode_t)
 	  NTSTATUS status = open_pipe ();
 	  if (NT_SUCCESS (status))
 	    {
-	      set_pipe_non_blocking (get_handle (), true);
+	      set_pipe_non_blocking (get_handle (), flags & O_NONBLOCK);
 	      if (!arm (write_ready))
 		res = error_set_errno;
 	      else
@@ -661,28 +662,84 @@ ssize_t __reg3
 fhandler_fifo::raw_write (const void *ptr, size_t len)
 {
   ssize_t ret = -1;
-  NTSTATUS status;
+  size_t nbytes = 0, chunk;
+  NTSTATUS status = STATUS_SUCCESS;
   IO_STATUS_BLOCK io;
+  HANDLE evt = NULL;
 
-  status = NtWriteFile (get_handle (), NULL, NULL, NULL, &io,
-			(PVOID) ptr, len, NULL, NULL);
-  if (NT_SUCCESS (status))
+  if (len <= max_atomic_write)
+    chunk = len;
+  else if (is_nonblocking ())
+    chunk = len = max_atomic_write;
+  else
+    chunk = max_atomic_write;
+
+  /* Create a wait event if the FIFO is in blocking mode. */
+  if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
+    return -1;
+
+  /* Write in chunks, accumulating a total.  If there's an error, just
+     return the accumulated total unless the first write fails, in
+     which case return -1. */
+  while (nbytes < len)
     {
-      /* NtWriteFile returns success with # of bytes written == 0 in
-	 case writing on a non-blocking pipe fails if the pipe buffer
-	 is full. */
-      if (io.Information == 0)
-	set_errno (EAGAIN);
+      ULONG_PTR nbytes_now = 0;
+      size_t left = len - nbytes;
+      size_t len1;
+      if (left > chunk)
+	len1 = chunk;
       else
-	ret = io.Information;
-    }
-  else if (STATUS_PIPE_IS_CLOSED (status))
-    {
-      set_errno (EPIPE);
-      raise (SIGPIPE);
+	len1 = left;
+      nbytes_now = 0;
+      status = NtWriteFile (get_handle (), evt, NULL, NULL, &io,
+			    (PVOID) ptr, len1, NULL, NULL);
+      if (evt && status == STATUS_PENDING)
+	{
+	  DWORD waitret = cygwait (evt, cw_infinite, cw_cancel | cw_sig_eintr);
+	  switch (waitret)
+	    {
+	    case WAIT_OBJECT_0:
+	      status = io.Status;
+	      break;
+	    case WAIT_SIGNALED:
+	      status = STATUS_THREAD_SIGNALED;
+	      break;
+	    case WAIT_CANCELED:
+	      status = STATUS_THREAD_CANCELED;
+	      break;
+	    default:
+	      break;
+	    }
+	}
+      if (NT_SUCCESS (status))
+	{
+	  nbytes_now = io.Information;
+	  /* NtWriteFile returns success with # of bytes written == 0
+	     if writing on a non-blocking pipe fails because the pipe
+	     buffer doesn't have sufficient space. */
+	  if (nbytes_now == 0)
+	    set_errno (EAGAIN);
+	  ptr = ((char *) ptr) + chunk;
+	  nbytes += nbytes_now;
+	}
+      else if (STATUS_PIPE_IS_CLOSED (status))
+	{
+	  set_errno (EPIPE);
+	  raise (SIGPIPE);
+	}
+      else
+	__seterrno_from_nt_status (status);
+      if (nbytes_now == 0)
+	len = 0;		/* Terminate loop. */
+      if (nbytes > 0)
+	ret = nbytes;
     }
-  else
-    __seterrno_from_nt_status (status);
+  if (evt)
+    CloseHandle (evt);
+  if (status == STATUS_THREAD_SIGNALED && !_my_tls.call_signal_handler ())
+    set_errno (EINTR);
+  else if (status == STATUS_THREAD_CANCELED)
+    pthread::static_cancel_self ();
   return ret;
 }
 
diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h
index b41b99b..e19cc8a 100644
--- a/winsup/cygwin/ntdll.h
+++ b/winsup/cygwin/ntdll.h
@@ -15,7 +15,9 @@
 extern GUID __cygwin_socket_guid;
 #define CYGWIN_SOCKET_GUID (&__cygwin_socket_guid)
 
-/* custom status code: */
+/* Custom Cygwin-only status codes. */
+#define STATUS_THREAD_SIGNALED	((NTSTATUS)0xe0000001)
+#define STATUS_THREAD_CANCELED	((NTSTATUS)0xe0000002)
 #define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269)
 
 /* Simplify checking for a transactional error code. */



More information about the Cygwin-cvs mailing list