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.
This commit is contained in:
Ken Brown 2019-03-25 23:06:10 +00:00 committed by Corinna Vinschen
parent 40db74128a
commit a137da74ba
2 changed files with 73 additions and 11 deletions

View File

@ -1253,6 +1253,10 @@ struct fifo_client_handler
HANDLE dummy_evt; /* Never signaled. */ HANDLE dummy_evt; /* Never signaled. */
fifo_client_handler () : fh (NULL), state (fc_unknown), connect_evt (NULL), fifo_client_handler () : fh (NULL), state (fc_unknown), connect_evt (NULL),
dummy_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 connect ();
int close (); int close ();
}; };
@ -1268,6 +1272,7 @@ class fhandler_fifo: public fhandler_base
fifo_client_handler client[MAX_CLIENTS]; fifo_client_handler client[MAX_CLIENTS];
int nclients, nconnected; int nclients, nconnected;
af_unix_spinlock_t _fifo_client_lock; af_unix_spinlock_t _fifo_client_lock;
bool _duplexer;
bool __reg2 wait (HANDLE); bool __reg2 wait (HANDLE);
NTSTATUS npfs_handle (HANDLE &); NTSTATUS npfs_handle (HANDLE &);
HANDLE create_pipe_instance (bool); HANDLE create_pipe_instance (bool);

View File

@ -33,7 +33,7 @@ STATUS_PIPE_EMPTY simply means there's no data to be read. */
fhandler_fifo::fhandler_fifo (): fhandler_fifo::fhandler_fifo ():
fhandler_base (), read_ready (NULL), write_ready (NULL), fhandler_base (), read_ready (NULL), write_ready (NULL),
listen_client_thr (NULL), lct_termination_evt (NULL), nclients (0), listen_client_thr (NULL), lct_termination_evt (NULL), nclients (0),
nconnected (0) nconnected (0), _duplexer (false)
{ {
pipe_name_buf[0] = L'\0'; pipe_name_buf[0] = L'\0';
need_fork_fixup (true); need_fork_fixup (true);
@ -224,6 +224,8 @@ fhandler_fifo::create_pipe_instance (bool first)
} }
access = GENERIC_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES access = GENERIC_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE; | SYNCHRONIZE;
if (first && _duplexer)
access |= GENERIC_WRITE;
sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
hattr = OBJ_INHERIT; hattr = OBJ_INHERIT;
if (first) if (first)
@ -437,7 +439,7 @@ fhandler_fifo::open (int flags, mode_t)
case O_RDWR: case O_RDWR:
reader = true; reader = true;
writer = false; writer = false;
duplexer = true; duplexer = _duplexer = true;
break; break;
default: default:
set_errno (EINVAL); 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); debug_only_printf ("reader %d, writer %d, duplexer %d", reader, writer, duplexer);
set_flags (flags); set_flags (flags);
if (reader) if (reader && !duplexer)
nohandle (true); nohandle (true);
/* Create control events for this named pipe */ /* Create control events for this named pipe */
@ -472,6 +474,48 @@ fhandler_fifo::open (int flags, mode_t)
goto out; 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 /* If we're reading, start the listen_client thread (which should
signal read_ready), and wait for a writer. */ signal read_ready), and wait for a writer. */
if (reader) if (reader)
@ -482,8 +526,8 @@ fhandler_fifo::open (int flags, mode_t)
res = error_errno_set; res = error_errno_set;
goto out; goto out;
} }
/* Wait for the listen_client thread to create the pipe and /* Wait for the listen_client thread to signal read_ready. This
signal read_ready. This should be quick. */ should be quick. */
HANDLE w[2] = { listen_client_thr, read_ready }; HANDLE w[2] = { listen_client_thr, read_ready };
switch (WaitForMultipleObjects (2, w, FALSE, INFINITE)) switch (WaitForMultipleObjects (2, w, FALSE, INFINITE))
{ {
@ -703,12 +747,25 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
fifo_client_unlock (); fifo_client_unlock ();
return; return;
} }
else if (nread < 0 && GetLastError () != ERROR_NO_DATA) /* In the duplex case with no data, we seem to get nread
{ == -1 with ERROR_PIPE_LISTENING on the first attempt to
fifo_client_unlock (); read from the duplex pipe (client[0]), and nread == 0
goto errout; on subsequent attempts. */
} else if (nread < 0)
else if (nread == 0) /* Client has disconnected. */ 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; client[i].state = fc_invalid;
nconnected--; nconnected--;