Fix --timeout

I'm not sure if it has ever worked correctly.  The line "lastWait =
after;" seems to mean that the timer was reset every time a build
produced log output.

Note that the timeout is now per build, as documented ("the maximum
number of seconds that a builder can run").
This commit is contained in:
Eelco Dolstra 2013-04-23 18:04:59 +02:00
parent f9974f856e
commit 772b70952f

View file

@ -186,9 +186,10 @@ struct Child
{ {
WeakGoalPtr goal; WeakGoalPtr goal;
set<int> fds; set<int> fds;
bool monitorForSilence; bool respectTimeouts;
bool inBuildSlot; bool inBuildSlot;
time_t lastOutput; /* time we last got output on stdout/stderr */ time_t lastOutput; /* time we last got output on stdout/stderr */
time_t timeStarted;
}; };
typedef map<pid_t, Child> Children; typedef map<pid_t, Child> Children;
@ -232,9 +233,6 @@ private:
/* Last time the goals in `waitingForAWhile' where woken up. */ /* Last time the goals in `waitingForAWhile' where woken up. */
time_t lastWokenUp; time_t lastWokenUp;
/* Last time `waitForInput' was last called. */
time_t lastWait;
public: public:
/* Set if at least one derivation had a BuildError (i.e. permanent /* Set if at least one derivation had a BuildError (i.e. permanent
@ -266,7 +264,7 @@ public:
/* Registers a running child process. `inBuildSlot' means that /* Registers a running child process. `inBuildSlot' means that
the process counts towards the jobs limit. */ the process counts towards the jobs limit. */
void childStarted(GoalPtr goal, pid_t pid, void childStarted(GoalPtr goal, pid_t pid,
const set<int> & fds, bool inBuildSlot, bool monitorForSilence); const set<int> & fds, bool inBuildSlot, bool respectTimeouts);
/* Unregisters a running child process. `wakeSleepers' should be /* Unregisters a running child process. `wakeSleepers' should be
false if there is no sense in waking up goals that are sleeping false if there is no sense in waking up goals that are sleeping
@ -2994,14 +2992,14 @@ unsigned Worker::getNrLocalBuilds()
void Worker::childStarted(GoalPtr goal, void Worker::childStarted(GoalPtr goal,
pid_t pid, const set<int> & fds, bool inBuildSlot, pid_t pid, const set<int> & fds, bool inBuildSlot,
bool monitorForSilence) bool respectTimeouts)
{ {
Child child; Child child;
child.goal = goal; child.goal = goal;
child.fds = fds; child.fds = fds;
child.lastOutput = time(0); child.timeStarted = child.lastOutput = time(0);
child.inBuildSlot = inBuildSlot; child.inBuildSlot = inBuildSlot;
child.monitorForSilence = monitorForSilence; child.respectTimeouts = respectTimeouts;
children[pid] = child; children[pid] = child;
if (inBuildSlot) nrLocalBuilds++; if (inBuildSlot) nrLocalBuilds++;
} }
@ -3117,32 +3115,23 @@ void Worker::waitForInput()
timeout.tv_usec = 0; timeout.tv_usec = 0;
time_t before = time(0); time_t before = time(0);
/* If a global timeout has been set, sleep until it's done. */ /* If we're monitoring for silence on stdout/stderr, or if there
if (settings.buildTimeout != 0) { is a build timeout, then wait for input until the first
useTimeout = true; deadline for any child. */
if (lastWait == 0 || lastWait > before) lastWait = before; assert(sizeof(time_t) >= sizeof(long));
timeout.tv_sec = std::max((time_t) 0, lastWait + settings.buildTimeout - before); time_t nearest = LONG_MAX; // nearest deadline
}
/* If we're monitoring for silence on stdout/stderr, sleep until
the first deadline for any child. */
if (settings.maxSilentTime != 0) {
time_t oldest = 0;
foreach (Children::iterator, i, children) { foreach (Children::iterator, i, children) {
if (i->second.monitorForSilence) { if (!i->second.respectTimeouts) continue;
oldest = oldest == 0 || i->second.lastOutput < oldest if (settings.maxSilentTime != 0)
? i->second.lastOutput : oldest; nearest = std::min(nearest, i->second.lastOutput + settings.maxSilentTime);
if (settings.buildTimeout != 0)
nearest = std::min(nearest, i->second.timeStarted + settings.buildTimeout);
} }
} if (nearest != LONG_MAX) {
if (oldest) { timeout.tv_sec = std::max((time_t) 1, nearest - before);
time_t silenceTimeout = std::max((time_t) 0, oldest + settings.maxSilentTime - before);
timeout.tv_sec = useTimeout
? std::min(silenceTimeout, timeout.tv_sec)
: silenceTimeout;
useTimeout = true; useTimeout = true;
printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec); printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
} }
}
/* If we are polling goals that are waiting for a lock, then wake /* If we are polling goals that are waiting for a lock, then wake
up after a few seconds at most. */ up after a few seconds at most. */
@ -3151,7 +3140,7 @@ void Worker::waitForInput()
if (lastWokenUp == 0) if (lastWokenUp == 0)
printMsg(lvlError, "waiting for locks or build slots..."); printMsg(lvlError, "waiting for locks or build slots...");
if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before; if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
timeout.tv_sec = std::max((time_t) 0, (time_t) (lastWokenUp + settings.pollInterval - before)); timeout.tv_sec = std::max((time_t) 1, (time_t) (lastWokenUp + settings.pollInterval - before));
} else lastWokenUp = 0; } else lastWokenUp = 0;
using namespace std; using namespace std;
@ -3175,9 +3164,6 @@ void Worker::waitForInput()
time_t after = time(0); time_t after = time(0);
/* Keep track of when we were last called. */
lastWait = after;
/* Process all available file descriptors. */ /* Process all available file descriptors. */
/* Since goals may be canceled from inside the loop below (causing /* Since goals may be canceled from inside the loop below (causing
@ -3218,7 +3204,7 @@ void Worker::waitForInput()
} }
if (settings.maxSilentTime != 0 && if (settings.maxSilentTime != 0 &&
j->second.monitorForSilence && j->second.respectTimeouts &&
after - j->second.lastOutput >= (time_t) settings.maxSilentTime) after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
{ {
printMsg(lvlError, printMsg(lvlError,
@ -3228,7 +3214,8 @@ void Worker::waitForInput()
} }
if (settings.buildTimeout != 0 && if (settings.buildTimeout != 0 &&
after - before >= (time_t) settings.buildTimeout) j->second.respectTimeouts &&
after - j->second.timeStarted >= (time_t) settings.buildTimeout)
{ {
printMsg(lvlError, printMsg(lvlError,
format("%1% timed out after %2% seconds") format("%1% timed out after %2% seconds")