about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/build.cc34
-rw-r--r--src/libstore/globals.cc1
-rw-r--r--src/libstore/globals.hh4
3 files changed, 35 insertions, 4 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 83bd6754a61a..4df62acea761 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -209,7 +209,10 @@ private:
 
     /* Last time the goals in `waitingForAWhile' where woken up. */
     time_t lastWokenUp;
-    
+
+    /* Last time `waitForInput' was last called.  */
+    time_t lastWait;
+
 public:
 
     bool cacheFailure;
@@ -681,7 +684,8 @@ HookInstance::HookInstance()
             builderOut.readSide.close();
             if (dup2(builderOut.writeSide, 4) == -1)
                 throw SysError("dupping builder's stdout/stderr");
-            
+
+	    /* XXX: Pass `buildTimeout' to the hook?  */
             execl(buildHook.c_str(), buildHook.c_str(), thisSystem.c_str(),
                 (format("%1%") % maxSilentTime).str().c_str(),
                 (format("%1%") % printBuildTrace).str().c_str(),
@@ -2666,7 +2670,14 @@ void Worker::waitForInput()
     struct timeval timeout;
     timeout.tv_usec = 0;
     time_t before = time(0);
-        
+
+    /* If a global timeout has been set, sleep until it's done.  */
+    if (buildTimeout != 0) {
+	useTimeout = true;
+        if (lastWait == 0 || lastWait > before) lastWait = before;
+	timeout.tv_sec = std::max((time_t) 0, lastWait + buildTimeout - before);
+    }
+
     /* If we're monitoring for silence on stdout/stderr, sleep until
        the first deadline for any child. */
     if (maxSilentTime != 0) {
@@ -2678,8 +2689,11 @@ void Worker::waitForInput()
             }
         }
         if (oldest) {
+	    time_t silenceTimeout = std::max((time_t) 0, oldest + maxSilentTime - before);
+            timeout.tv_sec = useTimeout
+		? std::min(silenceTimeout, timeout.tv_sec)
+		: silenceTimeout;
             useTimeout = true;
-            timeout.tv_sec = std::max((time_t) 0, oldest + maxSilentTime - before);
             printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
         }
     }
@@ -2717,6 +2731,9 @@ void Worker::waitForInput()
 
     time_t after = time(0);
 
+    /* Keep track of when we were last called.  */
+    lastWait = after;
+
     /* Process all available file descriptors. */
 
     /* Since goals may be canceled from inside the loop below (causing
@@ -2765,6 +2782,15 @@ void Worker::waitForInput()
                 % goal->getName() % maxSilentTime);
             goal->cancel();
         }
+
+	if (buildTimeout != 0 &&
+	    after - before >= (time_t) buildTimeout)
+        {
+            printMsg(lvlError,
+                format("%1% timed out after %2% seconds of activity")
+                % goal->getName() % buildTimeout);
+            goal->cancel();
+        }
     }
 
     if (!waitingForAWhile.empty() && lastWokenUp + wakeUpInterval <= after) {
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 7069d104aae4..2e9dc88237d1 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -26,6 +26,7 @@ unsigned int buildCores = 1;
 bool readOnlyMode = false;
 string thisSystem = "unset";
 time_t maxSilentTime = 0;
+time_t buildTimeout = 0;
 Paths substituters;
 bool useBuildHook = true;
 bool printBuildTrace = false;
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index a74a741d677e..231c1f8508ac 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -72,6 +72,10 @@ extern string thisSystem;
    infinity. */
 extern time_t maxSilentTime;
 
+/* The maximum duration in seconds that a builder can run.  0 means
+   infinity.  */
+extern time_t buildTimeout;
+
 /* The substituters.  There are programs that can somehow realise a
    store path without building, e.g., by downloading it or copying it
    from a CD. */