* select.cc (pipe_data_available): New function - uses NtQueryInformationFile

to return information about pipes.
(peek_pipe): Rewrite to use pipe_data_available for both read and write tests.
This commit is contained in:
Christopher Faylor 2011-06-01 00:57:49 +00:00
parent 2ec2d00e1b
commit a7a1247770
2 changed files with 88 additions and 105 deletions

View file

@ -1,3 +1,10 @@
2011-05-31 Christopher Faylor <me.cygwin2011@cgf.cx>
* select.cc (pipe_data_available): New function - uses
NtQueryInformationFile to return information about pipes.
(peek_pipe): Rewrite to use pipe_data_available for both read and write
tests.
2011-05-30 Christopher Faylor <me.cygwin2011@cgf.cx>
* dtable.cc (dtable::select_write): Add missing argument to

View file

@ -473,26 +473,71 @@ no_verify (select_record *, fd_set *, fd_set *, fd_set *)
return 0;
}
static int
pipe_data_available (int fd, fhandler_base *fh, HANDLE h, bool writing)
{
IO_STATUS_BLOCK iosb = {0};
FILE_PIPE_LOCAL_INFORMATION fpli = {0};
bool res = false;
if (NtQueryInformationFile (h,
&iosb,
&fpli,
sizeof (fpli),
FilePipeLocalInformation))
{
/* If NtQueryInformationFile fails, optimistically assume the
pipe is writable. This could happen if we somehow
inherit a pipe that doesn't permit FILE_READ_ATTRIBUTES
access on the write end. */
select_printf ("fd %d, %s, NtQueryInformationFile failed",
fd, fh->get_name ());
res = writing ? true : -1;
}
else if (!writing)
{
res = !!fpli.ReadDataAvailable;
select_printf ("fd %d, %s, read avail %u", fd, fh->get_name (), fpli.ReadDataAvailable);
}
else
{
/* If there is anything available in the pipe buffer then signal
that. This means that a pipe could still block since you could
be trying to write more to the pipe than is available in the
buffer but that is the hazard of select(). */
if ((fpli.WriteQuotaAvailable = (fpli.OutboundQuota - fpli.ReadDataAvailable)))
{
select_printf ("fd %d, %s, write: size %lu, avail %lu", fd,
fh->get_name (), fpli.OutboundQuota,
fpli.WriteQuotaAvailable);
res = true;
}
/* If we somehow inherit a tiny pipe (size < PIPE_BUF), then consider
the pipe writable only if it is completely empty, to minimize the
probability that a subsequent write will block. */
else if (fpli.OutboundQuota < PIPE_BUF &&
fpli.WriteQuotaAvailable == fpli.OutboundQuota)
{
select_printf ("fd, %s, write tiny pipe: size %lu, avail %lu",
fd, fh->get_name (), fpli.OutboundQuota,
fpli.WriteQuotaAvailable);
res = true;
}
}
return res;
}
static int
peek_pipe (select_record *s, bool from_select)
{
HANDLE h;
set_handle_or_return_if_not_open (h, s);
int n = 0;
int gotone = 0;
fhandler_base *fh = (fhandler_base *) s->fh;
/* Don't check if this is a non-blocking fd and I/O is still active.
That could give a false-positive with peek_pipe and friends. */
if (fh->has_ongoing_io ())
return 0;
/* Don't perform complicated tests if we don't need to. */
if (!s->read_selected && !s->except_selected)
goto out;
if (s->read_selected)
DWORD dev = fh->get_device ();
if (s->read_selected && dev != FH_PIPEW)
{
if (s->read_ready)
{
@ -524,106 +569,37 @@ peek_pipe (select_record *s, bool from_select)
gotone = s->read_ready = true;
goto out;
}
}
int n = pipe_data_available (s->fd, fh, h, false);
if (fh->get_device () == FH_PIPEW)
select_printf ("%s, select for read/except on write end of pipe",
fh->get_name ());
else if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL))
switch (GetLastError ())
{
case ERROR_BAD_PIPE:
case ERROR_PIPE_BUSY:
case ERROR_NO_DATA:
case ERROR_PIPE_NOT_CONNECTED:
n = 0;
break;
default:
select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ());
n = -1;
break;
}
if (n < 0)
{
select_printf ("%s, n %d", fh->get_name (), n);
if (s->except_selected)
gotone += s->except_ready = true;
if (s->read_selected)
gotone += s->read_ready = true;
}
if (n > 0 && s->read_selected)
{
select_printf ("%s, ready for read: avail %d", fh->get_name (), n);
gotone += s->read_ready = true;
}
if (!gotone && s->fh->hit_eof ())
{
select_printf ("%s, saw EOF", fh->get_name ());
if (s->except_selected)
gotone += s->except_ready = true;
if (s->read_selected)
gotone += s->read_ready = true;
if (n < 0)
{
select_printf ("read: %s, n %d", fh->get_name (), n);
if (s->except_selected)
gotone += s->except_ready = true;
if (s->read_selected)
gotone += s->read_ready = true;
}
else if (n > 0)
{
select_printf ("read: %s, ready for read: avail %d", fh->get_name (), n);
gotone += s->read_ready = true;
}
if (!gotone && s->fh->hit_eof ())
{
select_printf ("read: %s, saw EOF", fh->get_name ());
if (s->except_selected)
gotone += s->except_ready = true;
if (s->read_selected)
gotone += s->read_ready = true;
}
}
out:
if (s->write_selected)
if (s->write_selected && dev != FH_PIPER)
{
if (s->write_ready)
{
select_printf ("%s, already ready for write", fh->get_name ());
gotone++;
}
/* Do we need to do anything about SIGTTOU here? */
else if (fh->get_device () == FH_PIPER)
select_printf ("%s, select for write on read end of pipe",
fh->get_name ());
else
{
IO_STATUS_BLOCK iosb = {0};
FILE_PIPE_LOCAL_INFORMATION fpli = {0};
if (NtQueryInformationFile (h,
&iosb,
&fpli,
sizeof (fpli),
FilePipeLocalInformation))
{
/* If NtQueryInformationFile fails, optimistically assume the
pipe is writable. This could happen if we somehow
inherit a pipe that doesn't permit FILE_READ_ATTRIBUTES
access on the write end. */
select_printf ("%s, NtQueryInformationFile failed",
fh->get_name ());
gotone += s->write_ready = true;
}
/* If there is anything available in the pipe buffer then signal
that. This means that a pipe could still block since you could
be trying to write more to the pipe than is available in the
buffer but that is the hazard of select(). */
else if ((fpli.WriteQuotaAvailable = (fpli.OutboundQuota - fpli.ReadDataAvailable)))
{
select_printf ("%s, ready for write: size %lu, avail %lu",
fh->get_name (),
fpli.OutboundQuota,
fpli.WriteQuotaAvailable);
gotone += s->write_ready = true;
}
/* If we somehow inherit a tiny pipe (size < PIPE_BUF), then consider
the pipe writable only if it is completely empty, to minimize the
probability that a subsequent write will block. */
else if (fpli.OutboundQuota < PIPE_BUF &&
fpli.WriteQuotaAvailable == fpli.OutboundQuota)
{
select_printf ("%s, tiny pipe: size %lu, avail %lu",
fh->get_name (),
fpli.OutboundQuota,
fpli.WriteQuotaAvailable);
gotone += s->write_ready = true;
}
}
gotone += s->write_ready = pipe_data_available (s->fd, fh, h, true);
select_printf ("write: %s, gotone %d", fh->get_name (), gotone);
}
return gotone;
}