Cygwin: FIFO: fix and simplify listen_client_thread

Remove fifo_client_handler::connect and move its code into
listen_client_thread.  That way we can check the return status when a
client handler's connect_evt is signaled.  Previously we incorrectly
assumed there was a successful connection.

Also simplify listen_client_thread in the following ways:

- Replace fhandler_fifo::disconnect_and_reconnect by a new
  delete_client_handler method.  Now we just delete invalid client
  handlers rather than trying to re-use them.

- Try to maintain a client handler list that consists of connected
  client handlers and exactly one that is listening for a connection.
  This allows us to call WaitForMultipleObjects with only two wait
  objects.

- Remove 'dummy_evt' from the fifo_client_handler struct; it is no
  longer needed.

- On exit from listen_client_thread, delete the "extra" (listening)
  client handler.  Otherwise there could be a connection that doesn't
  get recorded in the client handler list.  This could happen when a
  file descriptor is being duplicated.
This commit is contained in:
Ken Brown 2019-04-14 19:16:04 +00:00 committed by Corinna Vinschen
parent bb46627871
commit 2b4cf7622e
2 changed files with 109 additions and 153 deletions

View File

@ -1250,13 +1250,10 @@ struct fifo_client_handler
fhandler_base *fh; fhandler_base *fh;
fifo_client_connect_state state; fifo_client_connect_state state;
HANDLE connect_evt; HANDLE connect_evt;
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) {}
fifo_client_handler (fhandler_base *_fh, fifo_client_connect_state _state, fifo_client_handler (fhandler_base *_fh, fifo_client_connect_state _state,
HANDLE _connect_evt, HANDLE _dummy_evt) HANDLE _connect_evt)
: fh (_fh), state (_state), connect_evt (_connect_evt), : fh (_fh), state (_state), connect_evt (_connect_evt) {}
dummy_evt (_dummy_evt) {}
int connect (); int connect ();
int close (); int close ();
}; };
@ -1278,8 +1275,8 @@ class fhandler_fifo: public fhandler_base
NTSTATUS npfs_handle (HANDLE &); NTSTATUS npfs_handle (HANDLE &);
HANDLE create_pipe_instance (bool); HANDLE create_pipe_instance (bool);
NTSTATUS open_pipe (); NTSTATUS open_pipe ();
int disconnect_and_reconnect (int);
int add_client_handler (); int add_client_handler ();
void delete_client_handler (int);
bool listen_client (); bool listen_client ();
int stop_listen_client (); int stop_listen_client ();
public: public:

View File

@ -118,62 +118,6 @@ set_pipe_non_blocking (HANDLE ph, bool nonblocking)
debug_printf ("NtSetInformationFile(FilePipeInformation): %y", status); debug_printf ("NtSetInformationFile(FilePipeInformation): %y", status);
} }
/* The pipe instance is always in blocking mode when this is called. */
int
fifo_client_handler::connect ()
{
NTSTATUS status;
IO_STATUS_BLOCK io;
if (connect_evt)
ResetEvent (connect_evt);
else if (!(connect_evt = create_event ()))
return -1;
status = NtFsControlFile (fh->get_handle (), connect_evt, NULL, NULL, &io,
FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0);
switch (status)
{
case STATUS_PENDING:
case STATUS_PIPE_LISTENING:
state = fc_connecting;
break;
case STATUS_PIPE_CONNECTED:
state = fc_connected;
set_pipe_non_blocking (fh->get_handle (), true);
break;
default:
__seterrno_from_nt_status (status);
return -1;
}
return 0;
}
int
fhandler_fifo::disconnect_and_reconnect (int i)
{
NTSTATUS status;
IO_STATUS_BLOCK io;
HANDLE ph = fc_handler[i].fh->get_handle ();
status = NtFsControlFile (ph, NULL, NULL, NULL, &io, FSCTL_PIPE_DISCONNECT,
NULL, 0, NULL, 0);
/* Short-lived. Don't use cygwait. We don't want to be interrupted. */
if (status == STATUS_PENDING
&& NtWaitForSingleObject (ph, FALSE, NULL) == WAIT_OBJECT_0)
status = io.Status;
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
set_pipe_non_blocking (fc_handler[i].fh->get_handle (), false);
if (fc_handler[i].connect () < 0)
return -1;
if (fc_handler[i].state == fc_connected)
nconnected++;
return 0;
}
NTSTATUS NTSTATUS
fhandler_fifo::npfs_handle (HANDLE &nph) fhandler_fifo::npfs_handle (HANDLE &nph)
{ {
@ -280,41 +224,49 @@ fhandler_fifo::open_pipe ()
int int
fhandler_fifo::add_client_handler () fhandler_fifo::add_client_handler ()
{ {
int ret = -1;
fifo_client_handler fc; fifo_client_handler fc;
fhandler_base *fh; fhandler_base *fh;
HANDLE ph = NULL;
bool first = (nhandlers == 0); bool first = (nhandlers == 0);
if (nhandlers == MAX_CLIENTS) if (nhandlers == MAX_CLIENTS)
{ {
set_errno (EMFILE); set_errno (EMFILE);
return -1; goto out;
} }
if (!(fc.dummy_evt = create_event ())) if (!(fc.connect_evt = create_event ()))
return -1; goto out;
if (!(fh = build_fh_dev (dev ()))) if (!(fh = build_fh_dev (dev ())))
{ {
set_errno (EMFILE); set_errno (EMFILE);
return -1; goto out;
} }
fc.fh = fh; ph = create_pipe_instance (first);
HANDLE ph = create_pipe_instance (first);
if (!ph) if (!ph)
goto errout; {
delete fh;
goto out;
}
else
{
fh->set_handle (ph); fh->set_handle (ph);
fh->set_flags (get_flags ()); fh->set_flags (get_flags ());
if (fc.connect () < 0) ret = 0;
{ fc.fh = fh;
fc.close ();
goto errout;
}
if (fc.state == fc_connected)
nconnected++;
fc_handler[nhandlers++] = fc; fc_handler[nhandlers++] = fc;
return 0; }
errout: out:
delete fh; return ret;
return -1; }
void
fhandler_fifo::delete_client_handler (int i)
{
fc_handler[i].close ();
if (i < --nhandlers)
memmove (fc_handler + i, fc_handler + i + 1,
(nhandlers - i) * sizeof (fc_handler[i]));
} }
/* Just hop to the listen_client_thread method. */ /* Just hop to the listen_client_thread method. */
@ -355,46 +307,23 @@ fhandler_fifo::listen_client_thread ()
while (1) while (1)
{ {
bool found; /* At the beginning of the loop, all client handlers are
HANDLE w[MAX_CLIENTS + 1]; in the fc_connected or fc_invalid state. */
int i;
DWORD wait_ret;
/* Delete any invalid clients. */
fifo_client_lock (); fifo_client_lock ();
found = false; int i = 0;
for (i = 0; i < nhandlers; i++) while (i < nhandlers)
switch (fc_handler[i].state)
{ {
case fc_invalid: if (fc_handler[i].state == fc_invalid)
if (disconnect_and_reconnect (i) < 0) delete_client_handler (i);
{
fifo_client_unlock ();
goto out;
}
else else
/* Recheck fc_handler[i].state. */ i++;
i--;
break;
case fc_connected:
w[i] = fc_handler[i].dummy_evt;
break;
case fc_connecting:
found = true;
w[i] = fc_handler[i].connect_evt;
break;
case fc_unknown: /* Shouldn't happen. */
default:
break;
} }
w[nhandlers] = lct_termination_evt;
int res = 0; /* Create a new client handler. */
if (!found) if (add_client_handler () < 0)
res = add_client_handler ();
fifo_client_unlock ();
if (res < 0)
goto out; goto out;
else if (!found)
continue;
/* Allow a writer to open. */ /* Allow a writer to open. */
if (!arm (read_ready)) if (!arm (read_ready))
@ -402,26 +331,73 @@ fhandler_fifo::listen_client_thread ()
__seterrno (); __seterrno ();
goto out; goto out;
} }
fifo_client_unlock ();
/* Wait for a client to connect. */ /* Listen for a writer to connect to the new client handler. */
wait_ret = WaitForMultipleObjects (nhandlers + 1, w, false, INFINITE); fifo_client_handler& fc = fc_handler[nhandlers - 1];
i = wait_ret - WAIT_OBJECT_0; do
if (i < 0 || i > nhandlers) {
NTSTATUS status;
IO_STATUS_BLOCK io;
status = NtFsControlFile (fc.fh->get_handle (), fc.connect_evt,
NULL, NULL, &io, FSCTL_PIPE_LISTEN,
NULL, 0, NULL, 0);
if (status == STATUS_PENDING)
{
HANDLE w[2] = { fc.connect_evt, lct_termination_evt };
DWORD waitret = WaitForMultipleObjects (2, w, false, INFINITE);
switch (waitret)
{
case WAIT_OBJECT_0:
status = io.Status;
break;
case WAIT_OBJECT_0 + 1:
ret = 0;
status = STATUS_THREAD_IS_TERMINATING;
break;
default:
__seterrno ();
debug_printf ("WaitForMultipleObjects failed, %E");
status = STATUS_THREAD_IS_TERMINATING;
break;
}
}
switch (status)
{
case STATUS_SUCCESS:
case STATUS_PIPE_CONNECTED:
fifo_client_lock ();
fc.state = fc_connected;
nconnected++;
set_pipe_non_blocking (fc.fh->get_handle (), true);
fifo_client_unlock ();
break;
case STATUS_PIPE_LISTENING:
/* Retry. */
fc.state = fc_connecting;
ResetEvent (fc.connect_evt);
break;
case STATUS_THREAD_IS_TERMINATING:
fifo_client_lock ();
delete_client_handler (nhandlers - 1);
fifo_client_unlock ();
goto out; goto out;
else if (i == nhandlers) /* Thread termination requested. */ default:
__seterrno_from_nt_status (status);
fifo_client_lock ();
delete_client_handler (nhandlers - 1);
fifo_client_unlock ();
goto out;
}
} while (fc.state == fc_connecting);
/* Check for thread termination in case WaitForMultipleObjects
didn't get called above. */
if (IsEventSignalled (lct_termination_evt))
{ {
ret = 0; ret = 0;
goto out; goto out;
} }
else
{
fifo_client_lock ();
fc_handler[i].state = fc_connected;
nconnected++;
set_pipe_non_blocking (fc_handler[i].fh->get_handle (), true);
fifo_client_unlock ();
yield ();
}
} }
out: out:
ResetEvent (read_ready); ResetEvent (read_ready);
@ -487,7 +463,7 @@ fhandler_fifo::open (int flags, mode_t)
/* If we're a duplexer, create the pipe and the first client handler. */ /* If we're a duplexer, create the pipe and the first client handler. */
if (duplexer) if (duplexer)
{ {
HANDLE ph, connect_evt, dummy_evt; HANDLE ph, connect_evt;
fhandler_base *fh; fhandler_base *fh;
ph = create_pipe_instance (true); ph = create_pipe_instance (true);
@ -513,16 +489,7 @@ fhandler_fifo::open (int flags, mode_t)
delete fh; delete fh;
goto out; goto out;
} }
if (!(dummy_evt = create_event ())) fc_handler[0] = fifo_client_handler (fh, fc_connected, connect_evt);
{
res = error_errno_set;
delete fh;
fh->close ();
CloseHandle (connect_evt);
goto out;
}
fc_handler[0] = fifo_client_handler (fh, fc_connected, connect_evt,
dummy_evt);
nconnected = nhandlers = 1; nconnected = nhandlers = 1;
} }
@ -863,8 +830,6 @@ fifo_client_handler::close ()
} }
if (connect_evt) if (connect_evt)
CloseHandle (connect_evt); CloseHandle (connect_evt);
if (dummy_evt)
CloseHandle (dummy_evt);
return res; return res;
} }
@ -943,10 +908,6 @@ fhandler_fifo::dup (fhandler_base *child, int flags)
|| !DuplicateHandle (GetCurrentProcess (), fc_handler[i].connect_evt, || !DuplicateHandle (GetCurrentProcess (), fc_handler[i].connect_evt,
GetCurrentProcess (), GetCurrentProcess (),
&fhf->fc_handler[i].connect_evt, &fhf->fc_handler[i].connect_evt,
0, true, DUPLICATE_SAME_ACCESS)
|| !DuplicateHandle (GetCurrentProcess (), fc_handler[i].dummy_evt,
GetCurrentProcess (),
&fhf->fc_handler[i].dummy_evt,
0, true, DUPLICATE_SAME_ACCESS)) 0, true, DUPLICATE_SAME_ACCESS))
{ {
CloseHandle (fhf->read_ready); CloseHandle (fhf->read_ready);
@ -975,7 +936,6 @@ fhandler_fifo::fixup_after_fork (HANDLE parent)
{ {
fc_handler[i].fh->fhandler_base::fixup_after_fork (parent); fc_handler[i].fh->fhandler_base::fixup_after_fork (parent);
fork_fixup (parent, fc_handler[i].connect_evt, "connect_evt"); fork_fixup (parent, fc_handler[i].connect_evt, "connect_evt");
fork_fixup (parent, fc_handler[i].dummy_evt, "dummy_evt");
} }
listen_client_thr = NULL; listen_client_thr = NULL;
lct_termination_evt = NULL; lct_termination_evt = NULL;
@ -992,6 +952,5 @@ fhandler_fifo::set_close_on_exec (bool val)
{ {
fc_handler[i].fh->fhandler_base::set_close_on_exec (val); fc_handler[i].fh->fhandler_base::set_close_on_exec (val);
set_no_inheritance (fc_handler[i].connect_evt, val); set_no_inheritance (fc_handler[i].connect_evt, val);
set_no_inheritance (fc_handler[i].dummy_evt, val);
} }
} }