Sockets and interrupt driven I/O
Chris Torek
chris at mimsy.umd.edu
Tue Mar 6 13:31:31 AEST 1990
In article <1011 at m1.cs.man.ac.uk> HoldswoS at r4.cs.man.ac.uk (Sean Holdsworth)
writes:
>... if I write to a socket and get back an EWOULDBLOCK error I have no
>way of knowing, other than polling, when the socket again becomes writable.
>I had hoped that when the state of the socket changed from blocked to
>unblocked that a SIGIO would be generated but from my experiments this
>appears not to be the case.
If you examine the file netinet/tcp_input.c, you will find code of the
form:
if (act > so->so_snd.sb_cc) {
tp->snd_wnd -= so->so_snd.sb_cc;
sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
ourfinisacked = 1;
} else {
sbdrop(&so->so_snd, acked);
tp->snd_wnd -= acked;
ourfinisacked = 0;
}
--> if ((so->so_snd.sb_flags & SB_WAIT) || so->so_snd.sb_sel)
sowwakeup(so);
tp->snd_una = ti->ti_ack;
The line marked `-->' above is the cuprit: For the sake of efficiency,
the TCP code calls sowwakeup(so) (a macro for sowakeup(so, &so->so_snd))
only if a process is waiting for data to go out, or is selecting, or was
selecting not long ago. When a process uses asynchronous I/O, however,
neither SB_WAIT nor sb_sel are set.
There are a number of ways around the problem. One of them involves no
kernel changes: simply arrange for some process to select on that socket.
Any process will do, including your own. Thus:
omask = sigblock(sigmask(SIGIO));
if ((n = write(socket_fd, buf, count)) < 0 && errno == EWOULDBLOCK) {
fd_set out;
struct timeval tv;
FD_ZERO(&out);
FD_SET(socket_fd, &out);
tv.tv_sec = 0;
tv.tv_usec = 0;
n = select(socket_fd + 1, (fd_set *)0, &out, (fd_set *)0, &tv);
if (n > 0) {
/* can write now, try again: must have unblocked
while we were fiddling with select() */
n = write(socket_fd, buf, count);
if (n < 0 && errno == EWOULDBLOCK)
... now what? ...
}
/* cannot write, but made so->so_snd.sb_sel non nil so that
the tcp code will call sowakeup() later */
n = 0; /* clobber the error */
}
if (n < 0)
... handle output error ...
(void) sigsetmask(omask);
Simpler fixes, if you have kernel source, are to remove the test from
tcp_input.c or to assert SB_WAIT in sosend() when returning EWOULDBLOCK.
(The latter would preserve efficiency, and protect any other code that
has the same bug as tcp_input.c, such as the XNS code [spp_usrreq.c].)
The bug has been fixed in 4.4BSD by removing the test from both
netinet/tcp_input.c and netns/spp_usrreq.c.
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain: chris at cs.umd.edu Path: uunet!mimsy!chris
More information about the Comp.unix.questions
mailing list