From b50f7d1f30991998564f62aa717862efb7555044 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Thu, 5 Apr 2012 15:44:36 +0200 Subject: Add -l N option to limit the load average. This is similar to GNU make -l/--load-average option. It limits the number of job started if the load average exceed the given value. It can be very useful when running ninja on a continuous integration server where we want to use parallelism as much as possible without overloading the server. --- src/build.cc | 4 +++- src/build.h | 5 ++++- src/ninja.cc | 13 +++++++++++-- src/util.cc | 29 +++++++++++++++++++++++++++++ src/util.h | 4 ++++ 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/build.cc b/src/build.cc index 6063cec..a269e50 100644 --- a/src/build.cc +++ b/src/build.cc @@ -452,7 +452,9 @@ void RealCommandRunner::Abort() { } bool RealCommandRunner::CanRunMore() { - return ((int)subprocs_.running_.size()) < config_.parallelism; + return ((int)subprocs_.running_.size()) < config_.parallelism + && ((subprocs_.running_.size() == 0 || config_.max_load_average <= 0.0f) + || GetLoadAverage() < config_.max_load_average); } bool RealCommandRunner::StartCommand(Edge* edge) { diff --git a/src/build.h b/src/build.h index 179fca6..9397e5f 100644 --- a/src/build.h +++ b/src/build.h @@ -98,7 +98,7 @@ struct CommandRunner { /// Options (e.g. verbosity, parallelism) passed to a build. struct BuildConfig { BuildConfig() : verbosity(NORMAL), dry_run(false), parallelism(1), - failures_allowed(1) {} + failures_allowed(1), max_load_average(-0.0f) {} enum Verbosity { NORMAL, @@ -109,6 +109,9 @@ struct BuildConfig { bool dry_run; int parallelism; int failures_allowed; + /// The maximum load average we must not exceed. A negative value + /// means that we do not have any limit. + double max_load_average; }; /// Builder wraps the build process: starting commands, updating status. diff --git a/src/ninja.cc b/src/ninja.cc index 7d020db..e149f98 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -84,6 +84,7 @@ void Usage(const BuildConfig& config) { " -f FILE specify input build file [default=build.ninja]\n" "\n" " -j N run N jobs in parallel [default=%d]\n" +" -l N do not start new jobs if the load average is greater than N\n" " -k N keep going until N jobs fail [default=1]\n" " -n dry run (don't run commands but pretend they succeeded)\n" " -v show all command lines while building\n" @@ -633,7 +634,7 @@ int main(int argc, char** argv) { int opt; while (tool.empty() && - (opt = getopt_long(argc, argv, "d:f:hj:k:nt:vC:", kLongOptions, + (opt = getopt_long(argc, argv, "d:f:hj:k:l:nt:vC:", kLongOptions, NULL)) != -1) { switch (opt) { case 'd': @@ -646,11 +647,19 @@ int main(int argc, char** argv) { case 'j': globals.config.parallelism = atoi(optarg); break; + case 'l': { + char* end; + double value = strtod(optarg, &end); + if (end == optarg) + Fatal("-l parameter not numeric: did you mean -l 0.0?"); + globals.config.max_load_average = value; + break; + } case 'k': { char* end; int value = strtol(optarg, &end, 10); if (*end != 0) - Fatal("-k parameter not numeric; did you mean -k0?"); + Fatal("-k parameter not numeric; did you mean -k 0?"); // We want to go until N jobs fail, which means we should allow // N failures and then stop. For N <= 0, INT_MAX is close enough diff --git a/src/util.cc b/src/util.cc index c88dc4e..73f13f8 100644 --- a/src/util.cc +++ b/src/util.cc @@ -293,3 +293,32 @@ string StripAnsiEscapeCodes(const string& in) { } return stripped; } + +#ifdef _WIN32 +static double GetLoadAverage_win32() +{ + // TODO(nicolas.despres@gmail.com): Find a way to implement it on Windows. + return -0.0f; +} +#else +static double GetLoadAverage_unix() +{ + double loadavg[3] = { 0.0f, 0.0f, 0.0f }; + if (getloadavg(loadavg, 3) < 0) + { + // Maybe we should return an error here or the availability of + // getloadavg(3) should be checked when ninja is configured. + return -0.0f; + } + return loadavg[0]; +} +#endif // _WIN32 + +double GetLoadAverage() +{ +#ifdef _WIN32 + return GetLoadAverage_win32(); +#else + return GetLoadAverage_unix(); +#endif // _WIN32 +} diff --git a/src/util.h b/src/util.h index 9b4e745..399913e 100644 --- a/src/util.h +++ b/src/util.h @@ -68,6 +68,10 @@ const char* SpellcheckString(const string& text, ...); /// Removes all Ansi escape codes (http://www.termsys.demon.co.uk/vtansi.htm). string StripAnsiEscapeCodes(const string& in); +/// @return the load average of the machine. A negative value is returned +/// on error. +double GetLoadAverage(); + #ifdef _MSC_VER #define snprintf _snprintf #define fileno _fileno -- cgit v0.12