about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-12-05T15·36+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-12-05T15·36+0000
commitc808e6252f7ba6d02834c591e0e4bcfbc8476635 (patch)
treee5cb6d7c878ec51a1d0160488703022a059d2cc4
parentfd4a9db91fc82ddc399f3be73ba65188a63cd7d9 (diff)
* Ugly hack to handle spurious SIGPOLLs.
-rw-r--r--src/nix-worker/main.cc70
1 files changed, 50 insertions, 20 deletions
diff --git a/src/nix-worker/main.cc b/src/nix-worker/main.cc
index 2f87ce2bf766..6a0dcc13d56c 100644
--- a/src/nix-worker/main.cc
+++ b/src/nix-worker/main.cc
@@ -49,6 +49,34 @@ static void tunnelStderr(const unsigned char * buf, size_t count)
 }
 
 
+/* Return true if the remote side has closed its end of the
+   connection, false otherwise.  Should not be called on any socket on
+   which we expect input! */
+static bool isFarSideClosed(int socket)
+{
+    struct timeval timeout;
+    timeout.tv_sec = timeout.tv_usec = 0;
+
+    fd_set fds;
+    FD_ZERO(&fds);
+    FD_SET(socket, &fds);
+        
+    if (select(socket + 1, &fds, 0, 0, &timeout) == -1)
+        throw SysError("select()");
+
+    if (!FD_ISSET(socket, &fds)) return false;
+
+    /* Destructive read to determine whether the select() marked the
+       socket as readable because there is actual input or because
+       we've reached EOF (i.e., a read of size 0 is available). */
+    char c;
+    if (read(socket, &c, 1) != 0)
+        throw Error("EOF expected (protocol error?)");
+    
+    return true;
+}
+
+
 /* A SIGPOLL 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
@@ -62,11 +90,27 @@ static void tunnelStderr(const unsigned char * buf, size_t count)
    time and wouldn't have to worry about races. */
 static void sigPollHandler(int sigNo)
 {
-    if (!blockInt) {
-        _isInterrupted = 1;
-        blockInt = 1;
-        canSendStderr = false;
-        write(STDERR_FILENO, "SIGPOLL\n", 8);
+    try {
+        /* Check that the far side actually closed.  We're still
+           getting spurious signals every once in a while.  I.e.,
+           there is no input available, but we get a signal with
+           POLL_IN set.  Maybe it's delayed or something. */
+        if (isFarSideClosed(from.fd)) {
+            if (!blockInt) {
+                _isInterrupted = 1;
+                blockInt = 1;
+                canSendStderr = false;
+                write(STDERR_FILENO, "SIGPOLL\n", 8);
+            }
+        } else {
+            string s = "spurious SIGPOLL\n";
+            write(STDERR_FILENO, s.c_str(), s.size());
+        }
+    }
+    catch (Error & e) {
+        /* Shouldn't happen. */
+        write(STDERR_FILENO, e.msg().c_str(), e.msg().size());
+        abort();
     }
 }
 
@@ -98,21 +142,7 @@ static void startWork()
        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(from.fd, &fds);
-        
-    if (select(from.fd + 1, &fds, 0, 0, &timeout) == -1)
-        throw SysError("select()");
-
-    if (FD_ISSET(from.fd, &fds)) {
-        char c;
-        if (read(from.fd, &c, 1) != 0)
-            throw Error("EOF expected (protocol error?)");
+    if (isFarSideClosed(from.fd)) {
         _isInterrupted = 1;
         checkInterrupt();
     }