If a FIFO is opened with O_RDWR access, create the pipe with
read/write access, and make the first client have the handle of that
pipe as its I/O handle.

Adjust fhandler_fifo::raw_read to account for the result of trying to
read from that client if there's no data.
---
 winsup/cygwin/fhandler.h       |  5 +++
 winsup/cygwin/fhandler_fifo.cc | 79 +++++++++++++++++++++++++++++-----
 2 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index ef34f9c40..3398cc625 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1253,6 +1253,10 @@ struct fifo_client_handler
   HANDLE dummy_evt;            /* Never signaled. */
   fifo_client_handler () : fh (NULL), state (fc_unknown), connect_evt (NULL),
                           dummy_evt (NULL) {}
+  fifo_client_handler (fhandler_base *_fh, fifo_client_connect_state _state,
+                      HANDLE _connect_evt, HANDLE _dummy_evt)
+    : fh (_fh), state (_state), connect_evt (_connect_evt),
+      dummy_evt (_dummy_evt) {}
   int connect ();
   int close ();
 };
@@ -1268,6 +1272,7 @@ class fhandler_fifo: public fhandler_base
   fifo_client_handler client[MAX_CLIENTS];
   int nclients, nconnected;
   af_unix_spinlock_t _fifo_client_lock;
+  bool _duplexer;
   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 2c20444c6..7847cca82 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -33,7 +33,7 @@ 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), nclients (0),
-  nconnected (0)
+  nconnected (0), _duplexer (false)
 {
   pipe_name_buf[0] = L'\0';
   need_fork_fixup (true);
@@ -224,6 +224,8 @@ fhandler_fifo::create_pipe_instance (bool first)
     }
   access = GENERIC_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
     | SYNCHRONIZE;
+  if (first && _duplexer)
+    access |= GENERIC_WRITE;
   sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
   hattr = OBJ_INHERIT;
   if (first)
@@ -437,7 +439,7 @@ fhandler_fifo::open (int flags, mode_t)
     case O_RDWR:
       reader = true;
       writer = false;
-      duplexer = true;
+      duplexer = _duplexer = true;
       break;
     default:
       set_errno (EINVAL);
@@ -447,7 +449,7 @@ fhandler_fifo::open (int flags, mode_t)
 
   debug_only_printf ("reader %d, writer %d, duplexer %d", reader, writer, 
duplexer);
   set_flags (flags);
-  if (reader)
+  if (reader && !duplexer)
     nohandle (true);
 
   /* Create control events for this named pipe */
@@ -472,6 +474,48 @@ fhandler_fifo::open (int flags, mode_t)
       goto out;
     }
 
+  /* If we're a duplexer, create the pipe and the first client. */
+  if (duplexer)
+    {
+      HANDLE ph, connect_evt, dummy_evt;
+      fhandler_base *fh;
+
+      ph = create_pipe_instance (true);
+      if (!ph)
+       {
+         res = error_errno_set;
+         goto out;
+       }
+      set_io_handle (ph);
+      set_pipe_non_blocking (ph, true);
+      if (!(fh = build_fh_dev (dev ())))
+       {
+         set_errno (EMFILE);
+         res = error_errno_set;
+         goto out;
+       }
+      fh->set_io_handle (ph);
+      fh->set_flags (flags);
+      if (!(connect_evt = create_event ()))
+       {
+         res = error_errno_set;
+         fh->close ();
+         delete fh;
+         goto out;
+       }
+      if (!(dummy_evt = create_event ()))
+       {
+         res = error_errno_set;
+         delete fh;
+         fh->close ();
+         CloseHandle (connect_evt);
+         goto out;
+       }
+      client[0] = fifo_client_handler (fh, fc_connected, connect_evt,
+                                      dummy_evt);
+      nconnected = nclients = 1;
+    }
+
   /* If we're reading, start the listen_client thread (which should
      signal read_ready), and wait for a writer. */
   if (reader)
@@ -482,8 +526,8 @@ fhandler_fifo::open (int flags, mode_t)
          res = error_errno_set;
          goto out;
        }
-      /* Wait for the listen_client thread to create the pipe and
-        signal read_ready.  This should be quick.  */
+      /* Wait for the listen_client thread to signal read_ready.  This
+        should be quick.  */
       HANDLE w[2] = { listen_client_thr, read_ready };
       switch (WaitForMultipleObjects (2, w, FALSE, INFINITE))
        {
@@ -703,12 +747,25 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
                fifo_client_unlock ();
                return;
              }
-           else if (nread < 0 && GetLastError () != ERROR_NO_DATA)
-             {
-               fifo_client_unlock ();
-               goto errout;
-             }
-           else if (nread == 0) /* Client has disconnected. */
+           /* In the duplex case with no data, we seem to get nread
+              == -1 with ERROR_PIPE_LISTENING on the first attempt to
+              read from the duplex pipe (client[0]), and nread == 0
+              on subsequent attempts. */
+           else if (nread < 0)
+             switch (GetLastError ())
+               {
+               case ERROR_NO_DATA:
+                 break;
+               case ERROR_PIPE_LISTENING:
+                 if (_duplexer && i == 0)
+                   break;
+                 /* Fall through. */
+               default:
+                 fifo_client_unlock ();
+                 goto errout;
+               }
+           else if (nread == 0 && (!_duplexer || i > 0))
+             /* Client has disconnected. */
              {
                client[i].state = fc_invalid;
                nconnected--;
-- 
2.17.0

Reply via email to