diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-12-03 03:03:36 +0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2006-12-03 03:03:36 +0000 |
commit | 3ed9e4ad9b72dfbe59d47823beec829fe550351e (patch) | |
tree | fc8a5086d483033f0a9d51a59de88b3f436fd531 /src | |
parent | 4251f94b32daed2abb0324439466876a97acdb77 (diff) |
* Some hardcore magic to handle asynchronous client disconnects.
The problem is that when we kill the client while the worker is
building, and the builder is not writing anything to stderr, then
the worker never notice that the socket is closed on the other side,
so it just continues indefinitely. The solution is to catch SIGIO,
which is sent when the far side of the socket closes, and simulate
an normal interruption. Of course, SIGIO is also sent every time
the client sends data over the socket, so we only enable the signal
handler when we're not expecting any data...
Diffstat (limited to 'src')
-rw-r--r-- | src/nix-worker/main.cc | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/src/nix-worker/main.cc b/src/nix-worker/main.cc index 17e892c64..c4a2d8a7a 100644 --- a/src/nix-worker/main.cc +++ b/src/nix-worker/main.cc @@ -6,6 +6,9 @@ #include "archive.hh" #include <iostream> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> using namespace nix; @@ -31,6 +34,10 @@ static Sink * _to; /* !!! should make writeToStderr an object */ bool canSendStderr; +/* This function is called anytime we want to write something to + stderr. If we're in a state where the protocol allows it (i.e., + when canSendStderr), send the message to the client over the + socket. */ static void tunnelStderr(const unsigned char * buf, size_t count) { writeFull(STDERR_FILENO, buf, count); @@ -48,11 +55,28 @@ static void tunnelStderr(const unsigned char * buf, size_t count) } +/* A SIGIO signal is received when data is available on the client + communication scoket, or when the client has closed its side of the + socket. This handler is enabled at precisely those moments in the + protocol when we're doing work and the client is supposed to be + quiet. Thus, if we get a SIGIO signal, it means that the client + has quit. So we should quit as well. */ +static void sigioHandler(int sigNo) +{ + _isInterrupted = 1; + canSendStderr = false; + write(STDERR_FILENO, "SIGIO\n", 6); +} + + /* startWork() means that we're starting an operation for which we want to send out stderr to the client. */ static void startWork() { canSendStderr = true; + + /* Handle client death asynchronously. */ + signal(SIGIO, sigioHandler); } @@ -60,6 +84,11 @@ static void startWork() client. */ 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); + canSendStderr = false; writeInt(STDERR_LAST, *_to); } @@ -178,7 +207,7 @@ static void processConnection(Source & from, Sink & to) writeInt(WORKER_MAGIC_2, to); debug("greeting exchanged"); - + _to = &to; canSendStderr = false; writeToStderr = tunnelStderr; @@ -216,6 +245,13 @@ void run(Strings args) if (arg == "--daemon") daemon = true; } + /* Allow us to receive SIGIO for events on the client socket. */ + signal(SIGIO, SIG_IGN); + if (fcntl(STDIN_FILENO, F_SETOWN, getpid()) == -1) + throw SysError("F_SETOWN"); + if (fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | FASYNC) == -1) + throw SysError("F_SETFL"); + if (slave) { FdSource source(STDIN_FILENO); FdSink sink(STDOUT_FILENO); |