summaryrefslogtreecommitdiffstats
path: root/Source/CTest/cmCTestMultiProcessHandler.cxx
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2018-09-09 15:11:38 (GMT)
committerBrad King <brad.king@kitware.com>2018-09-10 11:32:16 (GMT)
commit292ec157b67b5570d5bc2e00bba554cc157d9dae (patch)
tree47976098727357d9d9117ae3f320940ec8edd670 /Source/CTest/cmCTestMultiProcessHandler.cxx
parentf478fa633daeb1432805821adddc40730ffd283d (diff)
downloadCMake-292ec157b67b5570d5bc2e00bba554cc157d9dae.zip
CMake-292ec157b67b5570d5bc2e00bba554cc157d9dae.tar.gz
CMake-292ec157b67b5570d5bc2e00bba554cc157d9dae.tar.bz2
CTest: Fix --test-load regression
The `ctest --test-load` option is implemented in `StartNextTests` by not starting any tests when the load is too high and instead sleeping and then returning. Prior to commit v3.11.0-rc1~117^2 (CTest: Re-implement test process handling using libuv, 2017-12-10) our outer loop in `RunTests` would immediately call `StartNextTests` again. However, now the `uv_run` loop may simply terminate if there are no tests running because no events are left pending. Fix this by converting the sleep in `StartNextTests` into a libuv timer that it starts instead. This avoids leaving `uv_run` with no pending events. In the case that there are other running tests this also allows CTest to detect when they finish even if it during the wait period where we previously slept. This regression was not caught by the test suite because it only verified that we do not start new tests when the load was too high and not that we proceed to start tests when the load drops. Revise the test suite to cover both. Fixes: #18338
Diffstat (limited to 'Source/CTest/cmCTestMultiProcessHandler.cxx')
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx59
1 files changed, 39 insertions, 20 deletions
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index dcef8a0..322da23 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -5,7 +5,6 @@
#include "cmAffinity.h"
#include "cmCTest.h"
#include "cmCTestRunTest.h"
-#include "cmCTestScriptHandler.h"
#include "cmCTestTestHandler.h"
#include "cmSystemTools.h"
#include "cmWorkingDirectory.h"
@@ -53,6 +52,7 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
{
this->ParallelLevel = 1;
this->TestLoad = 0;
+ this->FakeLoadForTesting = 0;
this->Completed = 0;
this->RunningCount = 0;
this->ProcessorsAvailable = cmAffinity::GetProcessorsAvailable();
@@ -97,6 +97,16 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load)
{
this->TestLoad = load;
+
+ std::string fake_load_value;
+ if (cmSystemTools::GetEnv("__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING",
+ fake_load_value)) {
+ if (!cmSystemTools::StringToULong(fake_load_value.c_str(),
+ &this->FakeLoadForTesting)) {
+ cmSystemTools::Error("Failed to parse fake load value: ",
+ fake_load_value.c_str());
+ }
+ }
}
void cmCTestMultiProcessHandler::RunTests()
@@ -259,12 +269,19 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
void cmCTestMultiProcessHandler::StartNextTests()
{
- size_t numToStart = 0;
+ if (this->TestLoadRetryTimer.get() != nullptr) {
+ // This timer may be waiting to call StartNextTests again.
+ // Since we have been called it is no longer needed.
+ uv_timer_stop(this->TestLoadRetryTimer);
+ }
if (this->Tests.empty()) {
+ this->TestLoadRetryTimer.reset();
return;
}
+ size_t numToStart = 0;
+
if (this->RunningCount < this->ParallelLevel) {
numToStart = this->ParallelLevel - this->RunningCount;
}
@@ -280,7 +297,6 @@ void cmCTestMultiProcessHandler::StartNextTests()
}
bool allTestsFailedTestLoadCheck = false;
- bool usedFakeLoadForTesting = false;
size_t minProcessorsRequired = this->ParallelLevel;
std::string testWithMinProcessors;
@@ -293,15 +309,11 @@ void cmCTestMultiProcessHandler::StartNextTests()
allTestsFailedTestLoadCheck = true;
// Check for a fake load average value used in testing.
- std::string fake_load_value;
- if (cmSystemTools::GetEnv("__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING",
- fake_load_value)) {
- usedFakeLoadForTesting = true;
- if (!cmSystemTools::StringToULong(fake_load_value.c_str(),
- &systemLoad)) {
- cmSystemTools::Error("Failed to parse fake load value: ",
- fake_load_value.c_str());
- }
+ if (this->FakeLoadForTesting > 0) {
+ systemLoad = this->FakeLoadForTesting;
+ // Drop the fake load for the next iteration to a value low enough
+ // that the next iteration will start tests.
+ this->FakeLoadForTesting = 1;
}
// If it's not set, look up the true load average.
else {
@@ -385,18 +397,25 @@ void cmCTestMultiProcessHandler::StartNextTests()
}
cmCTestLog(this->CTest, HANDLER_OUTPUT, "*****" << std::endl);
- if (usedFakeLoadForTesting) {
- // Break out of the infinite loop of waiting for our fake load
- // to come down.
- this->StopTimePassed = true;
- } else {
- // Wait between 1 and 5 seconds before trying again.
- cmCTestScriptHandler::SleepInSeconds(cmSystemTools::RandomSeed() % 5 +
- 1);
+ // Wait between 1 and 5 seconds before trying again.
+ unsigned int milliseconds = (cmSystemTools::RandomSeed() % 5 + 1) * 1000;
+ if (this->FakeLoadForTesting) {
+ milliseconds = 10;
+ }
+ if (this->TestLoadRetryTimer.get() == nullptr) {
+ this->TestLoadRetryTimer.init(this->Loop, this);
}
+ this->TestLoadRetryTimer.start(
+ &cmCTestMultiProcessHandler::OnTestLoadRetryCB, milliseconds, 0);
}
}
+void cmCTestMultiProcessHandler::OnTestLoadRetryCB(uv_timer_t* timer)
+{
+ auto self = static_cast<cmCTestMultiProcessHandler*>(timer->data);
+ self->StartNextTests();
+}
+
void cmCTestMultiProcessHandler::FinishTestProcess(cmCTestRunTest* runner,
bool started)
{