Commit d648e0bf authored by Chad Austin's avatar Chad Austin Committed by Facebook Github Bot

use std::chrono for Subprocess timeouts

Summary: std::chrono is less prone to error than specifying timeouts in `int` seconds.

Reviewed By: yfeldblum, mzhaom

Differential Revision: D17767787

fbshipit-source-id: 5bb23d3a78e6e798e7b10331dc416e24d5a8746d
parent 816d933c
...@@ -652,7 +652,7 @@ ProcessReturnCode Subprocess::wait() { ...@@ -652,7 +652,7 @@ ProcessReturnCode Subprocess::wait() {
} while (found == -1 && errno == EINTR); } while (found == -1 && errno == EINTR);
// The only two remaining errors are ECHILD (other code reaped the // The only two remaining errors are ECHILD (other code reaped the
// child?), or EINVAL (cosmic rays?), and both merit an abort: // child?), or EINVAL (cosmic rays?), and both merit an abort:
PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)"; PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, 0)";
// Though the child process had quit, this call does not close the pipes // Though the child process had quit, this call does not close the pipes
// since its descendants may still be using them. // since its descendants may still be using them.
DCHECK_EQ(found, pid_); DCHECK_EQ(found, pid_);
...@@ -672,13 +672,14 @@ void Subprocess::sendSignal(int signal) { ...@@ -672,13 +672,14 @@ void Subprocess::sendSignal(int signal) {
checkUnixError(r, "kill"); checkUnixError(r, "kill");
} }
ProcessReturnCode Subprocess::terminateOrKill(int sigtermTimeoutSeconds) { ProcessReturnCode Subprocess::terminateOrKill(TimeoutDuration sigtermTimeout) {
returnCode_.enforce(ProcessReturnCode::RUNNING); returnCode_.enforce(ProcessReturnCode::RUNNING);
DCHECK_GT(pid_, 0) << "The subprocess has been waited already"; DCHECK_GT(pid_, 0) << "The subprocess has been waited already";
// 1. Send SIGTERM to kill the process // 1. Send SIGTERM to kill the process
terminate(); terminate();
// 2. check whether subprocess has terminated using non-blocking waitpid // 2. check whether subprocess has terminated using non-blocking waitpid
for (int i = 0; i < sigtermTimeoutSeconds * 10; i++) { auto pollUntil = std::chrono::steady_clock::now() + sigtermTimeout;
do {
int status; int status;
pid_t found; pid_t found;
// warp waitpid in the while loop to deal with EINTR. // warp waitpid in the while loop to deal with EINTR.
...@@ -686,11 +687,7 @@ ProcessReturnCode Subprocess::terminateOrKill(int sigtermTimeoutSeconds) { ...@@ -686,11 +687,7 @@ ProcessReturnCode Subprocess::terminateOrKill(int sigtermTimeoutSeconds) {
found = ::waitpid(pid_, &status, WNOHANG); found = ::waitpid(pid_, &status, WNOHANG);
} while (found == -1 && errno == EINTR); } while (found == -1 && errno == EINTR);
PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)"; PCHECK(found != -1) << "waitpid(" << pid_ << ", &status, WNOHANG)";
if (found == 0) { if (found) {
// The subprocess is still running, sleep for 100ms
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
} else {
// Just on the safe side, make sure it's the actual pid we are waiting. // Just on the safe side, make sure it's the actual pid we are waiting.
DCHECK_EQ(found, pid_); DCHECK_EQ(found, pid_);
returnCode_ = ProcessReturnCode::make(status); returnCode_ = ProcessReturnCode::make(status);
...@@ -699,7 +696,10 @@ ProcessReturnCode Subprocess::terminateOrKill(int sigtermTimeoutSeconds) { ...@@ -699,7 +696,10 @@ ProcessReturnCode Subprocess::terminateOrKill(int sigtermTimeoutSeconds) {
pid_ = -1; pid_ = -1;
return returnCode_; return returnCode_;
} }
}
// The subprocess is still running, sleep for 100ms
std::this_thread::sleep_for(std::chrono::milliseconds(100));
} while (std::chrono::steady_clock::now() <= pollUntil);
// 3. If we are at this point, we have waited enough time after // 3. If we are at this point, we have waited enough time after
// sending SIGTERM, we have to use nuclear option SIGKILL to kill // sending SIGTERM, we have to use nuclear option SIGKILL to kill
// the subprocess. // the subprocess.
......
...@@ -97,6 +97,7 @@ ...@@ -97,6 +97,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <chrono>
#include <exception> #include <exception>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -614,14 +615,15 @@ class Subprocess { ...@@ -614,14 +615,15 @@ class Subprocess {
sendSignal(SIGKILL); sendSignal(SIGKILL);
} }
using TimeoutDuration = std::chrono::milliseconds;
/** /**
* Send the SIGTERM to terminate the process, call `waitpid` * Send the SIGTERM to terminate the process, poll `waitpid` non-blockingly
* non-blockingly for several times, up to `sigtermTimeoutSeconds`. * several times up to `sigtermTimeout`. If the process hasn't terminated
* If the process hasn't terminated after that, send SIGKILL to kill * after that, send SIGKILL to kill the process and call `waitpid` blockingly.
* the process and call `waitpid` blockingly. Return the exit code of * Return the exit code of process.
* process.
*/ */
ProcessReturnCode terminateOrKill(int sigtermTimeoutSeconds); ProcessReturnCode terminateOrKill(TimeoutDuration sigtermTimeout);
//// ////
//// The methods below only affect the process's communication pipes, but //// The methods below only affect the process's communication pipes, but
......
...@@ -182,7 +182,7 @@ TEST(SimpleSubprocessTest, TerminateAfterProcessExit) { ...@@ -182,7 +182,7 @@ TEST(SimpleSubprocessTest, TerminateAfterProcessExit) {
Subprocess::Options().pipeStdout().pipeStderr()); Subprocess::Options().pipeStdout().pipeStderr());
const auto [stdout, stderr] = proc.communicate(); const auto [stdout, stderr] = proc.communicate();
EXPECT_EQ("hello\n", stdout); EXPECT_EQ("hello\n", stdout);
auto retCode = proc.terminateOrKill(1); auto retCode = proc.terminateOrKill(1s);
EXPECT_TRUE(retCode.exited()); EXPECT_TRUE(retCode.exited());
EXPECT_EQ(1, retCode.exitStatus()); EXPECT_EQ(1, retCode.exitStatus());
} }
...@@ -213,7 +213,7 @@ TEST(SimpleSubprocessTest, TerminateWithoutKill) { ...@@ -213,7 +213,7 @@ TEST(SimpleSubprocessTest, TerminateWithoutKill) {
"/bin/bash", "-c", "echo TerminateWithoutKill; sleep 60"}, "/bin/bash", "-c", "echo TerminateWithoutKill; sleep 60"},
Subprocess::Options().pipeStdout().pipeStderr()); Subprocess::Options().pipeStdout().pipeStderr());
EXPECT_TRUE(waitForAnyOutput(proc)); EXPECT_TRUE(waitForAnyOutput(proc));
auto retCode = proc.terminateOrKill(1); auto retCode = proc.terminateOrKill(1s);
EXPECT_TRUE(retCode.killed()); EXPECT_TRUE(retCode.killed());
EXPECT_EQ(SIGTERM, retCode.killSignal()); EXPECT_EQ(SIGTERM, retCode.killSignal());
} }
...@@ -231,7 +231,7 @@ TEST(SimpleSubprocessTest, KillAfterTerminate) { ...@@ -231,7 +231,7 @@ TEST(SimpleSubprocessTest, KillAfterTerminate) {
"trap \"sleep 120\" SIGTERM; echo KillAfterTerminate; sleep 60"}, "trap \"sleep 120\" SIGTERM; echo KillAfterTerminate; sleep 60"},
Subprocess::Options().pipeStdout().pipeStderr()); Subprocess::Options().pipeStdout().pipeStderr());
EXPECT_TRUE(waitForAnyOutput(proc)); EXPECT_TRUE(waitForAnyOutput(proc));
auto retCode = proc.terminateOrKill(1); auto retCode = proc.terminateOrKill(1s);
EXPECT_TRUE(retCode.killed()); EXPECT_TRUE(retCode.killed());
EXPECT_EQ(SIGKILL, retCode.killSignal()); EXPECT_EQ(SIGKILL, retCode.killSignal());
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment