Cygwin: FIFO: reduce I/O interleaving

Add a bool member 'last_read' to the fifo_client_handler structure,
which is set to true on a successful read.  This is used by raw_read
as follows.

When raw_read is called, it first locates the writer (if any) for
which last_read is true.  raw_read tries to read from that writer and
returns if there is input available.  Otherwise, it proceeds to poll
all the writers, as before.

The effect of this is that if a writer writes some data that is only
partially read, the next attempt to read will continue to read from
the same writer.  This should reduce the interleaving of output from
different writers.
This commit is contained in:
Ken Brown 2020-07-11 14:34:24 -04:00
parent e10425e1e3
commit 1c0cf5f4f9
2 changed files with 50 additions and 8 deletions

View File

@ -1298,7 +1298,8 @@ struct fifo_client_handler
{ {
HANDLE h; HANDLE h;
fifo_client_connect_state state; fifo_client_connect_state state;
fifo_client_handler () : h (NULL), state (fc_unknown) {} bool last_read; /* true if our last successful read was from this client. */
fifo_client_handler () : h (NULL), state (fc_unknown), last_read (false) {}
void close () { NtClose (h); } void close () { NtClose (h); }
fifo_client_connect_state set_state (); fifo_client_connect_state set_state ();
}; };

View File

@ -404,6 +404,7 @@ fhandler_fifo::update_my_handlers ()
goto out; goto out;
} }
fc.state = shared_fc_handler[i].state; fc.state = shared_fc_handler[i].state;
fc.last_read = shared_fc_handler[i].last_read;
} }
out: out:
set_prev_owner (null_fr_id); set_prev_owner (null_fr_id);
@ -1200,15 +1201,56 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
/* No one else can take ownership while we hold the reading_lock. */ /* No one else can take ownership while we hold the reading_lock. */
reading_lock (); reading_lock ();
take_ownership (); take_ownership ();
/* Poll the connected clients for input. */ /* Poll the connected clients for input. Make two passes. On
int nconnected = 0; the first pass, just try to read from the client from which
we last read successfully. This should minimize
interleaving of writes from different clients. */
fifo_client_lock (); fifo_client_lock ();
/* First pass. */
int j;
for (j = 0; j < nhandlers; j++)
if (fc_handler[j].last_read)
break;
if (j < nhandlers && fc_handler[j].state >= fc_closing)
{
NTSTATUS status;
IO_STATUS_BLOCK io;
status = NtReadFile (fc_handler[j].h, NULL, NULL, NULL,
&io, in_ptr, len, NULL, NULL);
switch (status)
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
/* io.Information is supposedly valid in latter case. */
if (io.Information > 0)
{
len = io.Information;
fifo_client_unlock ();
reading_unlock ();
return;
}
break;
case STATUS_PIPE_EMPTY:
break;
case STATUS_PIPE_BROKEN:
fc_handler[j].state = fc_disconnected;
break;
default:
debug_printf ("NtReadFile status %y", status);
fc_handler[j].state = fc_error;
break;
}
fc_handler[j].last_read = false;
}
/* Second pass. */
int nconnected = 0;
for (int i = 0; i < nhandlers; i++) for (int i = 0; i < nhandlers; i++)
if (fc_handler[i].state >= fc_closing) if (fc_handler[i].state >= fc_closing)
{ {
NTSTATUS status; NTSTATUS status;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
size_t nbytes = 0;
nconnected++; nconnected++;
status = NtReadFile (fc_handler[i].h, NULL, NULL, NULL, status = NtReadFile (fc_handler[i].h, NULL, NULL, NULL,
@ -1217,11 +1259,10 @@ fhandler_fifo::raw_read (void *in_ptr, size_t& len)
{ {
case STATUS_SUCCESS: case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW: case STATUS_BUFFER_OVERFLOW:
/* io.Information is supposedly valid. */ if (io.Information > 0)
nbytes = io.Information;
if (nbytes > 0)
{ {
len = nbytes; len = io.Information;
fc_handler[i].last_read = true;
fifo_client_unlock (); fifo_client_unlock ();
reading_unlock (); reading_unlock ();
return; return;