From a137da74bae803a4770dce9a6f38f1def582fe80 Mon Sep 17 00:00:00 2001 From: Ken Brown Date: Mon, 25 Mar 2019 23:06:10 +0000 Subject: [PATCH] Cygwin: FIFO: add support for the duplex case 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--;