Cygwin: FIFO: don't leave a pending listen request

On exit from the listen_client thread, make sure there's no pending
FSCTL_PIPE_LISTEN request.  Otherwise we might get a client connection
after restarting the thread, and we won't have a handle for
communicating with that client.

Remove the retry loop in the case of STATUS_PIPE_LISTENING; that case
shouldn't occur.

Remove the now-unused fc_connecting value from
fifo_client_connect_state.
This commit is contained in:
Ken Brown 2019-05-09 11:55:30 -04:00
parent 816c6da53a
commit c12053a793
2 changed files with 59 additions and 53 deletions

View File

@ -1240,7 +1240,6 @@ public:
enum fifo_client_connect_state enum fifo_client_connect_state
{ {
fc_unknown, fc_unknown,
fc_connecting,
fc_connected, fc_connected,
fc_invalid fc_invalid
}; };

View File

@ -272,10 +272,7 @@ listen_client_func (LPVOID param)
return fh->listen_client_thread (); return fh->listen_client_thread ();
} }
/* Start a thread that listens for client connections. Whenever a new /* Start a thread that listens for client connections. */
client connects, it creates a new pipe_instance if necessary.
(There may already be an available instance if a client has
disconnected.) */
bool bool
fhandler_fifo::listen_client () fhandler_fifo::listen_client ()
{ {
@ -331,70 +328,80 @@ fhandler_fifo::listen_client_thread ()
/* Create a new client handler. */ /* Create a new client handler. */
if (add_client_handler () < 0) if (add_client_handler () < 0)
goto out; {
fifo_client_unlock ();
goto out;
}
/* Allow a writer to open. */ /* Allow a writer to open. */
if (!arm (read_ready)) if (!arm (read_ready))
{ {
fifo_client_unlock ();
__seterrno (); __seterrno ();
goto out; goto out;
} }
fifo_client_unlock ();
/* Listen for a writer to connect to the new client handler. */ /* Listen for a writer to connect to the new client handler. */
fifo_client_handler& fc = fc_handler[nhandlers - 1]; fifo_client_handler& fc = fc_handler[nhandlers - 1];
do NTSTATUS status;
{ IO_STATUS_BLOCK io;
NTSTATUS status;
IO_STATUS_BLOCK io;
status = NtFsControlFile (fc.fh->get_handle (), fc.connect_evt, status = NtFsControlFile (fc.fh->get_handle (), fc.connect_evt,
NULL, NULL, &io, FSCTL_PIPE_LISTEN, NULL, NULL, &io, FSCTL_PIPE_LISTEN,
NULL, 0, NULL, 0); NULL, 0, NULL, 0);
if (status == STATUS_PENDING) fifo_client_unlock ();
if (status == STATUS_PENDING)
{
HANDLE w[2] = { fc.connect_evt, lct_termination_evt };
DWORD waitret = WaitForMultipleObjects (2, w, false, INFINITE);
switch (waitret)
{ {
HANDLE w[2] = { fc.connect_evt, lct_termination_evt }; case WAIT_OBJECT_0:
DWORD waitret = WaitForMultipleObjects (2, w, false, INFINITE); status = io.Status;
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:
record_connection (fc);
break; break;
case STATUS_PIPE_LISTENING: case WAIT_OBJECT_0 + 1:
/* Retry. */ ret = 0;
fc.state = fc_connecting; status = STATUS_THREAD_IS_TERMINATING;
ResetEvent (fc.connect_evt);
break; break;
case STATUS_THREAD_IS_TERMINATING:
fifo_client_lock ();
delete_client_handler (nhandlers - 1);
fifo_client_unlock ();
goto out;
default: default:
__seterrno_from_nt_status (status); __seterrno ();
fifo_client_lock (); debug_printf ("WaitForMultipleObjects failed, %E");
delete_client_handler (nhandlers - 1); status = STATUS_THREAD_IS_TERMINATING;
fifo_client_unlock (); break;
goto out;
} }
} while (fc.state == fc_connecting); }
HANDLE ph = NULL;
switch (status)
{
case STATUS_SUCCESS:
case STATUS_PIPE_CONNECTED:
record_connection (fc);
break;
case STATUS_THREAD_IS_TERMINATING:
/* Try to cancel the pending listen. Otherwise the first
writer to connect after the thread is restarted will be
invisible.
FIXME: Is there a direct way to do this? We do it by
opening and closing a write handle to the client side. */
open_pipe (ph);
/* We don't care about the return value of open_pipe. Even
if the latter failed, a writer might have connected. */
if (WaitForSingleObject (fc.connect_evt, 0) == WAIT_OBJECT_0
&& (NT_SUCCESS (io.Status) || io.Status == STATUS_PIPE_CONNECTED))
record_connection (fc);
else
fc.state = fc_invalid;
/* By closing ph we ensure that if fc connected to ph, fc
will be declared invalid on the next read attempt. */
if (ph)
CloseHandle (ph);
goto out;
default:
__seterrno_from_nt_status (status);
fc.state = fc_invalid;
goto out;
}
/* Check for thread termination in case WaitForMultipleObjects /* Check for thread termination in case WaitForMultipleObjects
didn't get called above. */ didn't get called above. */
if (IsEventSignalled (lct_termination_evt)) if (IsEventSignalled (lct_termination_evt))