This is the mail archive of the cygwin-patches mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 13/14] 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.
---
 winsup/cygwin/fhandler.h       |  1 +
 winsup/cygwin/fhandler_fifo.cc | 96 +++++++++++++++++++++++++++-------
 2 files changed, 79 insertions(+), 18 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index aea02c2b3..fd205a6db 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 fe4c67bdf..ce078d74d 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -22,6 +22,9 @@
 #include "ntdll.h"
 #include "cygwait.h"
 
+#define STATUS_THREAD_SIGNALED	((NTSTATUS)0xE0000001)
+#define STATUS_THREAD_CANCELED	((NTSTATUS)0xE0000002)
+
 /* This is only to be used for writers.  When reading,
 STATUS_PIPE_EMPTY simply means there's no data to be read. */
 #define STATUS_PIPE_IS_CLOSED(status)	\
@@ -39,7 +42,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 +563,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 +665,84 @@ ssize_t __reg3
 fhandler_fifo::raw_write (const void *ptr, size_t len)
 {
   ssize_t ret = -1;
+  size_t nbytes = 0, chunk;
   NTSTATUS status;
   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;
 }
 
-- 
2.17.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]