* Handle a subtle race condition: the client closing the socket

between the last worker read/write and the enabling of the signal
  handler.
This commit is contained in:
Eelco Dolstra 2006-12-03 03:16:27 +00:00
parent 3ed9e4ad9b
commit a9f9241054

View file

@ -76,7 +76,34 @@ static void startWork()
canSendStderr = true; canSendStderr = true;
/* Handle client death asynchronously. */ /* Handle client death asynchronously. */
signal(SIGIO, sigioHandler); if (signal(SIGIO, sigioHandler) == SIG_ERR)
throw SysError("setting handler for SIGIO");
/* Of course, there is a race condition here: the socket could
have closed between when we last read from / wrote to it, and
between the time we set the handler for SIGIO. In that case we
won't get the signal. So do a non-blocking select() to find
out if any input is available on the socket. If there is, it
has to be the 0-byte read that indicates that the socket has
closed. */
struct timeval timeout;
timeout.tv_sec = timeout.tv_usec = 0;
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
if (select(STDIN_FILENO + 1, &fds, 0, 0, &timeout) == -1)
throw SysError("select()");
if (FD_ISSET(STDIN_FILENO, &fds)) {
char c;
if (read(STDIN_FILENO, &c, 1) != 0)
throw Error("EOF expected (protocol error?)");
_isInterrupted = 1;
checkInterrupt();
}
} }
@ -87,7 +114,8 @@ static void stopWork()
/* Stop handling async client death; we're going to a state where /* Stop handling async client death; we're going to a state where
we're either sending or receiving from the client, so we'll be we're either sending or receiving from the client, so we'll be
notified of client death anyway. */ notified of client death anyway. */
signal(SIGIO, SIG_IGN); if (signal(SIGIO, SIG_IGN) == SIG_ERR)
throw SysError("ignoring SIGIO");
canSendStderr = false; canSendStderr = false;
writeInt(STDERR_LAST, *_to); writeInt(STDERR_LAST, *_to);