summaryrefslogtreecommitdiffstats
path: root/Source/CTest/cmCTestMultiProcessHandler.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CTest/cmCTestMultiProcessHandler.cxx')
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx173
1 files changed, 103 insertions, 70 deletions
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index ae07feb..53c47a2 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -9,10 +9,14 @@
#include "cmSystemTools.h"
#include "cmWorkingDirectory.h"
+#include "cm_uv.h"
+
#include "cmsys/FStream.hxx"
#include "cmsys/String.hxx"
#include "cmsys/SystemInformation.hxx"
+
#include <algorithm>
+#include <chrono>
#include <iomanip>
#include <list>
#include <math.h>
@@ -21,6 +25,43 @@
#include <stdlib.h>
#include <utility>
+#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \
+ UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19
+#define CMAKE_UV_SIGNAL_HACK
+/*
+ libuv does not use SA_RESTART on its signal handler, but C++ streams
+ depend on it for reliable i/o operations. This RAII helper convinces
+ libuv to install its handler, and then revises the handler to add the
+ SA_RESTART flag. We use a distinct uv loop that never runs to avoid
+ ever really getting a callback. libuv may fill the hack loop's signal
+ pipe and then stop writing, but that won't break any real loops.
+ */
+class cmUVSignalHackRAII
+{
+ uv_loop_t HackLoop;
+ cm::uv_signal_ptr HackSignal;
+ static void HackCB(uv_signal_t*, int) {}
+public:
+ cmUVSignalHackRAII()
+ {
+ uv_loop_init(&this->HackLoop);
+ this->HackSignal.init(this->HackLoop);
+ this->HackSignal.start(HackCB, SIGCHLD);
+ struct sigaction hack_sa;
+ sigaction(SIGCHLD, NULL, &hack_sa);
+ if (!(hack_sa.sa_flags & SA_RESTART)) {
+ hack_sa.sa_flags |= SA_RESTART;
+ sigaction(SIGCHLD, &hack_sa, NULL);
+ }
+ }
+ ~cmUVSignalHackRAII()
+ {
+ this->HackSignal.stop();
+ uv_loop_close(&this->HackLoop);
+ }
+};
+#endif
+
class TestComparator
{
public:
@@ -95,24 +136,32 @@ void cmCTestMultiProcessHandler::RunTests()
if (this->HasCycles) {
return;
}
+#ifdef CMAKE_UV_SIGNAL_HACK
+ cmUVSignalHackRAII hackRAII;
+#endif
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
+
+ uv_loop_init(&this->Loop);
this->StartNextTests();
- while (!this->Tests.empty()) {
- if (this->StopTimePassed) {
- return;
- }
- this->CheckOutput();
- this->StartNextTests();
- }
- // let all running tests finish
- while (this->CheckOutput()) {
- }
+ uv_run(&this->Loop, UV_RUN_DEFAULT);
+ uv_loop_close(&this->Loop);
+
this->MarkFinished();
this->UpdateCostData();
}
-void cmCTestMultiProcessHandler::StartTestProcess(int test)
+bool cmCTestMultiProcessHandler::StartTestProcess(int test)
{
+ std::chrono::system_clock::time_point stop_time = this->CTest->GetStopTime();
+ if (stop_time != std::chrono::system_clock::time_point() &&
+ stop_time <= std::chrono::system_clock::now()) {
+ cmCTestLog(this->CTest, ERROR_MESSAGE, "The stop time has been passed. "
+ "Stopping all tests."
+ << std::endl);
+ this->StopTimePassed = true;
+ return false;
+ }
+
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"test " << test << "\n", this->Quiet);
this->TestRunningMap[test] = true; // mark the test as running
@@ -120,7 +169,7 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test)
this->EraseTest(test);
this->RunningCount += GetProcessorsUsed(test);
- cmCTestRunTest* testRun = new cmCTestRunTest(this->TestHandler);
+ cmCTestRunTest* testRun = new cmCTestRunTest(*this);
if (this->CTest->GetRepeatUntilFail()) {
testRun->SetRunUntilFailOn();
testRun->SetNumberOfRuns(this->CTest->GetTestRepeat());
@@ -143,28 +192,11 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test)
this->LockResources(test);
if (testRun->StartTest(this->Total)) {
- this->RunningTests.insert(testRun);
- } else if (testRun->IsStopTimePassed()) {
- this->StopTimePassed = true;
- delete testRun;
- return;
- } else {
-
- for (auto& j : this->Tests) {
- j.second.erase(test);
- }
-
- this->UnlockResources(test);
- this->Completed++;
- this->TestFinishMap[test] = true;
- this->TestRunningMap[test] = false;
- this->RunningCount -= GetProcessorsUsed(test);
- testRun->EndTest(this->Completed, this->Total, false);
- if (!this->Properties[test]->Disabled) {
- this->Failed->push_back(this->Properties[test]->Name);
- }
- delete testRun;
+ return true;
}
+
+ this->FinishTestProcess(testRun, false);
+ return false;
}
void cmCTestMultiProcessHandler::LockResources(int index)
@@ -222,8 +254,7 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
// if there are no depends left then run this test
if (this->Tests[test].empty()) {
- this->StartTestProcess(test);
- return true;
+ return this->StartTestProcess(test);
}
// This test was not able to start because it is waiting
// on depends to run
@@ -233,6 +264,11 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
void cmCTestMultiProcessHandler::StartNextTests()
{
size_t numToStart = 0;
+
+ if (this->Tests.empty()) {
+ return;
+ }
+
if (this->RunningCount < this->ParallelLevel) {
numToStart = this->ParallelLevel - this->RunningCount;
}
@@ -365,45 +401,42 @@ void cmCTestMultiProcessHandler::StartNextTests()
}
}
-bool cmCTestMultiProcessHandler::CheckOutput()
+void cmCTestMultiProcessHandler::FinishTestProcess(cmCTestRunTest* runner,
+ bool started)
{
- // no more output we are done
- if (this->RunningTests.empty()) {
- return false;
- }
- std::vector<cmCTestRunTest*> finished;
- std::string out, err;
- for (cmCTestRunTest* p : this->RunningTests) {
- if (!p->CheckOutput()) {
- finished.push_back(p);
- }
- }
- for (cmCTestRunTest* p : finished) {
- this->Completed++;
- int test = p->GetIndex();
+ this->Completed++;
+
+ int test = runner->GetIndex();
+ auto properties = runner->GetTestProperties();
- bool testResult = p->EndTest(this->Completed, this->Total, true);
- if (p->StartAgain()) {
+ bool testResult = runner->EndTest(this->Completed, this->Total, started);
+ if (started) {
+ if (runner->StartAgain()) {
this->Completed--; // remove the completed test because run again
- continue;
- }
- if (testResult) {
- this->Passed->push_back(p->GetTestProperties()->Name);
- } else {
- this->Failed->push_back(p->GetTestProperties()->Name);
- }
- for (auto& t : this->Tests) {
- t.second.erase(test);
+ return;
}
- this->TestFinishMap[test] = true;
- this->TestRunningMap[test] = false;
- this->RunningTests.erase(p);
- this->WriteCheckpoint(test);
- this->UnlockResources(test);
- this->RunningCount -= GetProcessorsUsed(test);
- delete p;
}
- return true;
+
+ if (testResult) {
+ this->Passed->push_back(properties->Name);
+ } else if (!properties->Disabled) {
+ this->Failed->push_back(properties->Name);
+ }
+
+ for (auto& t : this->Tests) {
+ t.second.erase(test);
+ }
+
+ this->TestFinishMap[test] = true;
+ this->TestRunningMap[test] = false;
+ this->WriteCheckpoint(test);
+ this->UnlockResources(test);
+ this->RunningCount -= GetProcessorsUsed(test);
+
+ delete runner;
+ if (started) {
+ this->StartNextTests();
+ }
}
void cmCTestMultiProcessHandler::UpdateCostData()
@@ -670,7 +703,7 @@ void cmCTestMultiProcessHandler::PrintTestList()
cmWorkingDirectory workdir(p.Directory);
- cmCTestRunTest testRun(this->TestHandler);
+ cmCTestRunTest testRun(*this);
testRun.SetIndex(p.Index);
testRun.SetTestProperties(&p);
testRun.ComputeArguments(); // logs the command in verbose mode