summaryrefslogtreecommitdiffstats
path: root/Source/CTest
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2024-03-06 16:16:33 (GMT)
committerBrad King <brad.king@kitware.com>2024-03-10 15:41:39 (GMT)
commit5de1e21659090ca83e39e223d351e353347eb88e (patch)
tree1f618443c6abf98712619a5cb33a35c64439ab74 /Source/CTest
parentbbcbcff7d94bb3dd4b47ed5484207dc960911f4c (diff)
downloadCMake-5de1e21659090ca83e39e223d351e353347eb88e.zip
CMake-5de1e21659090ca83e39e223d351e353347eb88e.tar.gz
CMake-5de1e21659090ca83e39e223d351e353347eb88e.tar.bz2
ctest: Allow passing -j without value to choose a contextual default
Under job server integration, added by commit 80fe56c481 (ctest: Add support for running under a make job server on POSIX systems, 2023-11-15, v3.29.0-rc1~324^2), use a very high default so that parallelism is effectively limited only by available job server tokens. Otherwise, choose a default limit based on the number of processors. Also allow passing `0` to specify unbounded parallelism. Fixes: #25739
Diffstat (limited to 'Source/CTest')
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx66
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.h12
-rw-r--r--Source/CTest/cmCTestTestCommand.cxx4
-rw-r--r--Source/CTest/cmCTestTestCommand.h4
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx18
5 files changed, 87 insertions, 17 deletions
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 6efe008..53c5f76 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -43,6 +43,17 @@
#include "cmUVJobServerClient.h"
#include "cmWorkingDirectory.h"
+namespace {
+// For unspecified parallelism, limit to the number of processors,
+// but with a minimum greater than 1 so there is some parallelism.
+constexpr unsigned long kParallelLevelMinimum = 2u;
+
+// For "unbounded" parallelism, limit to a very high value.
+// Under a job server, parallelism is effectively limited
+// only by available job server tokens.
+constexpr unsigned long kParallelLevelUnbounded = 0x10000u;
+}
+
namespace cmsys {
class RegularExpression;
}
@@ -72,6 +83,7 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler(
, TestHandler(handler)
, ProcessorsAvailable(cmAffinity::GetProcessorsAvailable())
, HaveAffinity(this->ProcessorsAvailable.size())
+ , ParallelLevelDefault(kParallelLevelMinimum)
{
}
@@ -97,9 +109,43 @@ void cmCTestMultiProcessHandler::SetTests(TestMap tests,
}
// Set the max number of tests that can be run at the same time.
-void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
+void cmCTestMultiProcessHandler::SetParallelLevel(cm::optional<size_t> level)
+{
+ this->ParallelLevel = level;
+
+ if (!this->ParallelLevel) {
+ // '-j' was given with no value. Limit by number of processors.
+ cmsys::SystemInformation info;
+ info.RunCPUCheck();
+ unsigned long processorCount = info.GetNumberOfLogicalCPU();
+
+ if (cm::optional<std::string> fakeProcessorCount =
+ cmSystemTools::GetEnvVar(
+ "__CTEST_FAKE_PROCESSOR_COUNT_FOR_TESTING")) {
+ unsigned long pc = 0;
+ if (cmStrToULong(*fakeProcessorCount, &pc)) {
+ processorCount = pc;
+ } else {
+ cmSystemTools::Error("Failed to parse fake processor count: " +
+ *fakeProcessorCount);
+ }
+ }
+
+ this->ParallelLevelDefault =
+ std::max(kParallelLevelMinimum, processorCount);
+ }
+}
+
+size_t cmCTestMultiProcessHandler::GetParallelLevel() const
{
- this->ParallelLevel = level < 1 ? 1 : level;
+ if ((this->ParallelLevel && *this->ParallelLevel == 0) ||
+ (!this->ParallelLevel && this->JobServerClient)) {
+ return kParallelLevelUnbounded;
+ }
+ if (this->ParallelLevel) {
+ return *this->ParallelLevel;
+ }
+ return this->ParallelLevelDefault;
}
void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load)
@@ -446,10 +492,11 @@ void cmCTestMultiProcessHandler::UnlockResources(int index)
inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test)
{
size_t processors = static_cast<int>(this->Properties[test]->Processors);
+ size_t const parallelLevel = this->GetParallelLevel();
// If processors setting is set higher than the -j
// setting, we default to using all of the process slots.
- if (processors > this->ParallelLevel) {
- processors = this->ParallelLevel;
+ if (processors > parallelLevel) {
+ processors = parallelLevel;
}
// Cap tests that want affinity to the maximum affinity available.
if (this->HaveAffinity && processors > this->HaveAffinity &&
@@ -503,8 +550,9 @@ void cmCTestMultiProcessHandler::StartNextTests()
size_t numToStart = 0;
- if (this->RunningCount < this->ParallelLevel) {
- numToStart = this->ParallelLevel - this->RunningCount;
+ size_t const parallelLevel = this->GetParallelLevel();
+ if (this->RunningCount < parallelLevel) {
+ numToStart = parallelLevel - this->RunningCount;
}
if (numToStart == 0) {
@@ -518,7 +566,7 @@ void cmCTestMultiProcessHandler::StartNextTests()
}
bool allTestsFailedTestLoadCheck = false;
- size_t minProcessorsRequired = this->ParallelLevel;
+ size_t minProcessorsRequired = this->GetParallelLevel();
std::string testWithMinProcessors;
cmsys::SystemInformation info;
@@ -813,7 +861,7 @@ void cmCTestMultiProcessHandler::ReadCostData()
this->Properties[index]->PreviousRuns = prev;
// When not running in parallel mode, don't use cost data
- if (this->ParallelLevel > 1 && this->Properties[index] &&
+ if (this->GetParallelLevel() > 1 && this->Properties[index] &&
this->Properties[index]->Cost == 0) {
this->Properties[index]->Cost = cost;
}
@@ -842,7 +890,7 @@ int cmCTestMultiProcessHandler::SearchByName(std::string const& name)
void cmCTestMultiProcessHandler::CreateTestCostList()
{
- if (this->ParallelLevel > 1) {
+ if (this->GetParallelLevel() > 1) {
this->CreateParallelTestCostList();
} else {
this->CreateSerialTestCostList();
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index bbbfc7b..d4759f1 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -63,7 +63,7 @@ public:
// Set the tests
void SetTests(TestMap tests, PropertiesMap properties);
// Set the max number of tests that can be run at the same time.
- void SetParallelLevel(size_t);
+ void SetParallelLevel(cm::optional<size_t> level);
void SetTestLoad(unsigned long load);
virtual void RunTests();
void PrintOutputAsJson();
@@ -201,7 +201,15 @@ private:
ResourceAvailabilityErrors;
cmCTestResourceAllocator ResourceAllocator;
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
- size_t ParallelLevel = 1; // max number of process that can be run at once
+
+ // Get the maximum number of processors that may be used at once.
+ size_t GetParallelLevel() const;
+
+ // With no '-j' option, default to serial testing.
+ cm::optional<size_t> ParallelLevel = 1;
+
+ // Fallback parallelism limit when '-j' is given with no value.
+ size_t ParallelLevelDefault;
// 'make' jobserver client. If connected, we acquire a token
// for each test before running its process.
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index ed16354..98ce862 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -105,8 +105,8 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
if (this->StopOnFailure) {
handler->SetOption("StopOnFailure", "ON");
}
- if (!this->ParallelLevel.empty()) {
- handler->SetOption("ParallelLevel", this->ParallelLevel);
+ if (this->ParallelLevel) {
+ handler->SetOption("ParallelLevel", *this->ParallelLevel);
}
if (!this->Repeat.empty()) {
handler->SetOption("Repeat", this->Repeat);
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h
index ce054ed..23661c5 100644
--- a/Source/CTest/cmCTestTestCommand.h
+++ b/Source/CTest/cmCTestTestCommand.h
@@ -8,7 +8,9 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
+#include "cmArgumentParserTypes.h"
#include "cmCTestHandlerCommand.h"
#include "cmCommand.h"
@@ -56,7 +58,7 @@ protected:
std::string ExcludeFixture;
std::string ExcludeFixtureSetup;
std::string ExcludeFixtureCleanup;
- std::string ParallelLevel;
+ cm::optional<ArgumentParser::Maybe<std::string>> ParallelLevel;
std::string Repeat;
std::string ScheduleRandom;
std::string StopTime;
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 921ab1e..61a18a2 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -548,9 +548,21 @@ bool cmCTestTestHandler::ProcessOptions()
return false;
}
}
- if (this->GetOption("ParallelLevel")) {
- this->CTest->SetParallelLevel(
- std::stoi(*this->GetOption("ParallelLevel")));
+ if (cmValue parallelLevel = this->GetOption("ParallelLevel")) {
+ if (parallelLevel.IsEmpty()) {
+ // An empty value tells ctest to choose a default.
+ this->CTest->SetParallelLevel(cm::nullopt);
+ } else {
+ // A non-empty value must be a non-negative integer.
+ unsigned long plevel = 0;
+ if (!cmStrToULong(*parallelLevel, &plevel)) {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "ParallelLevel invalid value: " << *parallelLevel
+ << std::endl);
+ return false;
+ }
+ this->CTest->SetParallelLevel(plevel);
+ }
}
if (this->GetOption("StopOnFailure")) {