4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-02-08 10:09:32 +08:00

Cygwin: AF_UNIX: select: fix some errors

Try to make the implementation compatible with Stevens, UNIX Network
Programming.

select.cc:peek_socket_unix now calls
fhandler_socket_unix::binding_state and
fhandler_socket_unix::so_error, so these need to be made public.

One glaring remaining problem is that our current implementation of
datagram sockets makes it impossible to check whether a write would
block.  The problem is that we have no send buffer until we connect to
a pipe.

Also change the use of except_on_write so that it is set only if the
connect state is connect_pending when select is called.  Use this to
check whether an async connect has completed, which implies ready for
writing according to Stevens.
This commit is contained in:
Ken Brown 2021-04-08 17:39:59 -04:00
parent 3c00e4c762
commit 266a70c09f
3 changed files with 65 additions and 33 deletions

View File

@ -1156,9 +1156,7 @@ class fhandler_socket_unix : public fhandler_socket
void scm_fd_unlock () { shmem->scm_fd_unlock (); }
bind_state binding_state (bind_state val)
{ return shmem->binding_state (val); }
bind_state binding_state () const { return shmem->binding_state (); }
int so_error (int err) { return shmem->so_error (err); }
int so_error () const { return shmem->so_error (); }
bool so_passcred (bool pc) { return shmem->so_passcred (pc); }
bool so_passcred () const { return shmem->so_passcred (); }
int reuseaddr (int err) { return shmem->reuseaddr (err); }
@ -1263,6 +1261,8 @@ class fhandler_socket_unix : public fhandler_socket
int saw_shutdown () const { return shmem->shutdown (); }
bool saw_shutdown_read () const { return !!(saw_shutdown () & _SHUT_RECV); }
bool saw_shutdown_write () const { return !!(saw_shutdown () & _SHUT_SEND); }
int so_error () const { return shmem->so_error (); }
bind_state binding_state () const { return shmem->binding_state (); }
int open (int flags, mode_t mode = 0);
int close ();

View File

@ -3208,6 +3208,9 @@ fhandler_socket_unix::sendmsg (const struct msghdr *msg, int flags)
set_errno (EPROTOTYPE);
__leave;
}
/* FIXME: Should we maintain our own send buffer(s) so that
writing doesn't have to block while we wait for a pipe
connection? */
status = open_pipe (ph, &pipe_name);
if (STATUS_PIPE_NO_INSTANCE_AVAILABLE (status))
{

View File

@ -1959,15 +1959,24 @@ peek_socket_unix (select_record *me, bool)
{
select_printf ("already ready for read");
gotone = 1;
goto out;
goto check_write;
}
if (fh->get_unread ())
{
select_printf ("ready for read");
gotone += me->read_ready = true;
goto out;
goto check_write;
}
if (fh->connect_state () == connected)
if (fh->so_error ())
{
select_printf ("pending error, so ready for read");
gotone += me->read_ready = true;
goto check_write;
}
if ((fh->get_socket_type () == SOCK_STREAM
&& fh->connect_state () == connected)
|| (fh->get_socket_type () == SOCK_DGRAM
&& fh->binding_state () == bound))
while (1)
{
PFILE_PIPE_PEEK_BUFFER pbuf
@ -1985,9 +1994,7 @@ peek_socket_unix (select_record *me, bool)
{
select_printf ("read: saw shutdown");
gotone += me->read_ready = true;
if (me->except_selected)
gotone += me->except_ready = true;
goto out;
goto check_write;
}
continue;
}
@ -1996,30 +2003,20 @@ peek_socket_unix (select_record *me, bool)
select_printf ("read: ready for read: avail %d",
packet->data_len);
gotone += me->read_ready = true;
goto out;
}
else if (fh->get_socket_type () == SOCK_DGRAM)
{
select_printf ("read: ready for read: 0-length datagram packet");
gotone += me->read_ready = true;
goto out;
goto check_write;
}
else
{
select_printf ("read: 0-length stream socket packet");
select_printf ("read: ready for read: 0-length packet");
gotone += me->read_ready = true;
if (me->except_selected)
gotone += me->except_ready = true;
goto out;
goto check_write;
}
}
else if (!NT_SUCCESS (status))
{
select_printf ("peek_pipe status %y", status);
gotone += me->read_ready = true;
if (me->except_selected)
gotone += me->except_ready = true;
goto out;
goto check_write;
}
else
break;
@ -2041,32 +2038,60 @@ peek_socket_unix (select_record *me, bool)
select_printf ("NtQueryInformationFile failed, status %y",
status);
gotone += me->read_ready = true;
goto out;
goto check_write;
}
if (fpli.NamedPipeState != FILE_PIPE_LISTENING_STATE)
{
select_printf ("pipe state %d", fpli.NamedPipeState);
gotone += me->read_ready = true;
goto out;
goto check_write;
}
}
}
out:
check_write:
if (me->write_selected)
{
if (me->write_ready)
{
select_printf ("already ready for write");
gotone += 1;
goto out;
}
else
if (fh->so_error ())
{
select_printf ("pending error, so ready for write");
gotone += me->write_ready = true;
goto out;
}
if (me->except_on_write && fh->connect_state () == connected)
{
select_printf ("async connect completed, so ready for write");
gotone += me->write_ready = true;
goto out;
}
if (!me->read_selected)
fh->grab_admin_pkt ();
if (fh->saw_shutdown_write ())
{
select_printf ("write: saw shutdown");
gotone += me->write_ready = true;
goto out;
}
/* FIXME: With our current implementation of datagram sockets,
we have no send buffer, so we can't adequately test whether a
write would block. We might block waiting for a pipe
connection. */
if (fh->get_socket_type () == SOCK_STREAM
&& fh->connect_state () == connected)
{
/* FIXME: I'm not calling pipe_data_available because its
test for space available in the buffer doesn't make sense
to me. Also, we probably don't want to consider the
socket writable if there's only one byte available in the
buffer. But maybe I've gone too far in insisting on
MAX_AF_PKT_LEN bytes available. */
MAX_AF_PKT_LEN bytes available. Stevens says there
should be SO_SNDLOWAT bytes available, but we don't seem
to set this. */
IO_STATUS_BLOCK io;
FILE_PIPE_LOCAL_INFORMATION fpli;
NTSTATUS status
@ -2079,14 +2104,17 @@ out:
status);
/* See the comment in pipe_data_available. */
gotone += me->write_ready = true;
goto out;
}
else if (fpli.WriteQuotaAvailable >= MAX_AF_PKT_LEN)
if (fpli.WriteQuotaAvailable >= MAX_AF_PKT_LEN)
{
select_printf ("read for write");
gotone += me->write_ready = true;
goto out;
}
}
}
out:
return gotone;
}
@ -2160,8 +2188,6 @@ socket_unix_cleanup (select_record *, select_stuff *stuff)
stuff->device_specific_socket_unix = NULL;
}
/* FIXME: Where in what follows should we be checking so_error? */
select_record *
fhandler_socket_unix::select_read (select_stuff *ss)
{
@ -2175,7 +2201,7 @@ fhandler_socket_unix::select_read (select_stuff *ss)
s->cleanup = socket_unix_cleanup;
s->read_selected = true;
grab_admin_pkt ();
s->read_ready = saw_shutdown_read ());
s->read_ready = saw_shutdown_read () || so_error ();
return s;
}
@ -2191,8 +2217,11 @@ fhandler_socket_unix::select_write (select_stuff *ss)
s->verify = verify_ok;
s->cleanup = socket_unix_cleanup;
s->write_selected = true;
s->write_ready = saw_shutdown_write () || connect_state () == unconnected;
if (connect_state () != unconnected)
s->write_ready = saw_shutdown_write () || so_error ()
|| (connect_state () == unconnected && get_socket_type () == SOCK_STREAM);
/* Contrary to the wsock case, we will use except_on_write only to
check for a successful async connect. */
if (connect_state () != connect_pending)
s->except_on_write = true;
return s;
}