execute_process: Restore termination of processes on timeout

Since commit 5420639a8d (cmExecuteProcessCommand: Replace cmsysProcess
with cmUVProcessChain, 2023-06-01, v3.28.0-rc1~138^2~8) we have not
actually terminated child processes on an `execute_process` timeout.
Similarly for other migrations from cmsysProcess to cmUVProcessChain.

Teach cmUVProcessChain clients that implement timeouts to actually
terminate remaining child processes when the timeout is reached.

Fixes: #27378
This commit is contained in:
Brad King
2025-11-11 09:06:49 -05:00
parent 63465e8858
commit 22e84c7e7f
7 changed files with 30 additions and 0 deletions

View File

@@ -122,6 +122,8 @@ bool cmCTestBuildAndTest::RunTest(std::vector<std::string> const& argv,
std::cout << "\n*** Exception executing: " << exception.second;
} break;
}
} else {
chain.Terminate();
}
return result;

View File

@@ -1020,6 +1020,7 @@ bool cmCTestBuildHandler::RunMakeCommand(std::string const& command,
break;
}
} else {
chain.Terminate();
cmCTestOptionalLog(this->CTest, WARNING,
"There was a timeout" << std::endl, this->Quiet);
}

View File

@@ -1007,6 +1007,7 @@ bool cmCTest::RunMakeCommand(std::string const& command, std::string& output,
break;
}
} else {
chain.Terminate();
cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
}
@@ -3385,6 +3386,7 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
bool result = true;
if (timedOut) {
chain.Terminate();
char const* error_str = "Process terminated due to timeout\n";
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
stdErr->append(error_str, strlen(error_str));

View File

@@ -405,6 +405,9 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
!(chain.Finished() && outputData.Finished && errorData.Finished)) {
uv_run(&chain.GetLoop(), UV_RUN_ONCE);
}
if (timedOut) {
chain.Terminate();
}
if (!arguments.OutputQuiet &&
(arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
processOutput.DecodeText(std::string(), strdata, 1);

View File

@@ -985,6 +985,7 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
bool result = true;
if (timedOut) {
chain.Terminate();
char const* error_str = "Process terminated due to timeout\n";
if (outputflag != OUTPUT_NONE) {
std::cerr << error_str << std::endl;

View File

@@ -12,6 +12,8 @@
#include <cm3p/uv.h>
#include "cmsys/Process.h"
#include "cm_fileno.hxx"
#include "cmGetPipes.h"
@@ -58,6 +60,7 @@ struct cmUVProcessChain::InternalData
cmUVProcessChainBuilder::ProcessConfiguration const& config, bool first,
bool last);
void Finish();
void Terminate();
};
cmUVProcessChainBuilder::cmUVProcessChainBuilder() = default;
@@ -403,6 +406,15 @@ void cmUVProcessChain::InternalData::Finish()
this->Valid = true;
}
void cmUVProcessChain::InternalData::Terminate()
{
for (std::unique_ptr<ProcessData> const& p : this->Processes) {
if (!p->ProcessStatus.Finished) {
cmsysProcess_KillPID(static_cast<unsigned long>(p->Process->pid));
}
}
}
cmUVProcessChain::cmUVProcessChain()
: Data(cm::make_unique<InternalData>())
{
@@ -487,6 +499,11 @@ bool cmUVProcessChain::Finished() const
return this->Data->ProcessesCompleted >= this->Data->Processes.size();
}
void cmUVProcessChain::Terminate()
{
this->Data->Terminate();
}
std::pair<cmUVProcessChain::ExceptionCode, std::string>
cmUVProcessChain::Status::GetException() const
{

View File

@@ -117,6 +117,10 @@ public:
Status const& GetStatus(std::size_t index) const;
bool Finished() const;
/** Terminate any remaining child processes.
Call this only after exiting the event loop, and at most once. */
void Terminate();
private:
friend class cmUVProcessChainBuilder;