diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-12-03 03:16:27 +0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-12-03 03:16:27 +0000 |
commit | a9f92410541e15e994c3306215608cb33ff101e2 (patch) | |
tree | 76b2624ca04c2773585c7e65dd07b303899b38bf /src | |
parent | 3ed9e4ad9b72dfbe59d47823beec829fe550351e (diff) |
* Handle a subtle race condition: the client closing the socket
between the last worker read/write and the enabling of the signal
handler.
Diffstat (limited to 'src')
-rw-r--r-- | src/nix-worker/main.cc | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/src/nix-worker/main.cc b/src/nix-worker/main.cc index c4a2d8a7a..666f61572 100644 --- a/src/nix-worker/main.cc +++ b/src/nix-worker/main.cc @@ -76,7 +76,34 @@ static void startWork() canSendStderr = true; /* 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 we're either sending or receiving from the client, so we'll be notified of client death anyway. */ - signal(SIGIO, SIG_IGN); + if (signal(SIGIO, SIG_IGN) == SIG_ERR) + throw SysError("ignoring SIGIO"); canSendStderr = false; writeInt(STDERR_LAST, *_to); |