diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 9d76dd49b..84df30344 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -95,12 +95,16 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test) std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory(); cmSystemTools::ChangeDirectory(this->Properties[test]->Directory.c_str()); + // Lock the resources we'll be using + this->LockResources(test); + if(testRun->StartTest(this->Total)) { this->RunningTests.insert(testRun); } else { + this->UnlockResources(test); this->Completed++; this->TestFinishMap[test] = true; this->TestRunningMap[test] = false; @@ -112,6 +116,25 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test) cmSystemTools::ChangeDirectory(current_dir.c_str()); } +//--------------------------------------------------------- +void cmCTestMultiProcessHandler::LockResources(int index) +{ + this->LockedResources.insert( + this->Properties[index]->LockedResources.begin(), + this->Properties[index]->LockedResources.end()); +} + +//--------------------------------------------------------- +void cmCTestMultiProcessHandler::UnlockResources(int index) +{ + for(std::set::iterator i = + this->Properties[index]->LockedResources.begin(); + i != this->Properties[index]->LockedResources.end(); ++i) + { + this->LockedResources.erase(*i); + } +} + //--------------------------------------------------------- void cmCTestMultiProcessHandler::EraseTest(int test) { @@ -146,6 +169,17 @@ inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test) //--------------------------------------------------------- bool cmCTestMultiProcessHandler::StartTest(int test) { + //Check for locked resources + for(std::set::iterator i = + this->Properties[test]->LockedResources.begin(); + i != this->Properties[test]->LockedResources.end(); ++i) + { + if(this->LockedResources.find(*i) != this->LockedResources.end()) + { + return false; + } + } + // copy the depend tests locally because when // a test is finished it will be removed from the depend list // and we don't want to be iterating a list while removing from it @@ -274,7 +308,7 @@ bool cmCTestMultiProcessHandler::CheckOutput() this->TestRunningMap[test] = false; this->RunningTests.erase(p); this->WriteCheckpoint(test); - + this->UnlockResources(test); this->RunningCount -= GetProcessorsUsed(test); delete p; } diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index ebec6c7da..d4f6c7102 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -55,7 +55,7 @@ public: cmCTestTestHandler * GetTestHandler() { return this->TestHandler; } -protected: +protected: // Start the next test or tests as many as are allowed by // ParallelLevel void StartNextTests(); @@ -83,6 +83,9 @@ protected: bool CheckCycles(); int FindMaxIndex(); inline size_t GetProcessorsUsed(int index); + + void LockResources(int index); + void UnlockResources(int index); // map from test number to set of depend tests TestMap Tests; TestCostMap TestCosts; @@ -99,6 +102,7 @@ protected: std::vector* Passed; std::vector* Failed; std::vector LastTestsFailed; + std::set LockedResources; std::vector* TestResults; size_t ParallelLevel; // max number of process that can be run at once std::set RunningTests; // current running tests diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index fa9794a8e..659cb73e5 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -448,7 +448,7 @@ bool cmCTestRunTest::StartTest(size_t total) //---------------------------------------------------------------------- void cmCTestRunTest::ComputeArguments() { - std::vector::const_iterator j = + std::vector::const_iterator j = this->TestProperties->Args.begin(); ++j; // skip test name @@ -463,7 +463,7 @@ void cmCTestRunTest::ComputeArguments() } else { - this->ActualCommand = + this->ActualCommand = this->TestHandler->FindTheExecutable( this->TestProperties->Args[1].c_str()); ++j; //skip the executable (it will be actualCommand) diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index e36074f28..b9cee6c65 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -2099,6 +2099,17 @@ bool cmCTestTestHandler::SetTestsProperties( rtit->AttachOnFail.push_back(*f); } } + if ( key == "RESOURCE_LOCK" ) + { + std::vector lval; + cmSystemTools::ExpandListArgument(val.c_str(), lval); + + for(std::vector::iterator f = lval.begin(); + f != lval.end(); ++f) + { + rtit->LockedResources.insert(*f); + } + } if ( key == "TIMEOUT" ) { rtit->Timeout = atof(val.c_str()); diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 05bdf867b..704956468 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -104,6 +104,7 @@ public: int Processors; std::vector Environment; std::vector Labels; + std::set LockedResources; }; struct cmCTestTestResult diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index b52bc19eb..4e9b9734b 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx @@ -142,6 +142,12 @@ void cmTest::DefineProperties(cmake *cm) "Specify a list of text labels associated with a test.", "The list is reported in dashboard submissions."); + cm->DefineProperty + ("RESOURCE_LOCK", cmProperty::TEST, + "Specify a list of resources that are locked by this test.", + "If multiple tests specify the same resource lock, they are guaranteed " + "not to run concurrently."); + cm->DefineProperty ("MEASUREMENT", cmProperty::TEST, "Specify a CDASH measurement and value to be reported for a test.",