在超时模式下以异步方式运行使用Boost进程的进程 [英] Running a process using boost process in async mode with timeout
问题描述
在下面的代码中,我试图实现一个运行shell命令并获取stdio
,stderr
并返回代码的程序.我正在按照建议
In the following code, I am trying to implement a program that runs a shell command and get the stdio
, stderr
and return code. I am doing it using boost process
in the async
mode as advised here.
namespace bp = boost::process;
class Process {
public:
Process(std::string & cmd, const int timeout);
void run();
private:
void timeout_handler();
const std::string command;
const int timeout;
bool killed;
bool stopped;
std::string stdOut;
std::string stdErr;
int returnStatus;
boost::asio::io_service ios;
boost::process::group group;
boost::asio::deadline_timer deadline_timer;
};
Process::Process(std::string & cmd, const int timeout):
command(cmd),
timeout(timeout),
deadline_timer(ios)
{}
void Process::timeout_handler()
{
if (stopped)
return;
if (deadline_timer.expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
std::cout << "Time Up!" << std::endl;
group.terminate();
std::cout << "Killed the process and all its decendents" << std::endl;
killed = true;
stopped = true;
deadline_timer.expires_at(boost::posix_time::pos_infin);
}
deadline_timer.async_wait(std::bind(&Process::timeout_handler, this));
}
void Process::run()
{
std::future<std::string> dataOut;
std::future<std::string> dataErr;
bp::child c(command, bp::std_in.close(), bp::std_out > dataOut, bp::std_err > dataErr, ios, group);
deadline_timer.expires_from_now(boost::posix_time::seconds(timeout));
deadline_timer.async_wait(std::bind(&Process::timeout_handler, this));
ios.run();
c.wait();
stdOut = dataOut.get();
stdErr = dataErr.get();
returnStatus = c.exit_code();
}
int main(int argc, char** argv)
{
if(argc < 2)
{
std::cout << "Usage: \na.out <command>" << std::endl;
exit(1);
}
std::vector<std::string> arguments(argv + 1, argv + argc);
std::string command;
for( const auto & tok : arguments)
{
command += tok + " ";
}
std::cout << command << std::endl;
Process p(command, 10);
p.run();
return 0;
}
现在,以上代码仅在deadline_timer
到期后返回.我想要的是,如果子进程在计时器到期之前完成或者它(以及它分叉的所有子进程)应该终止,则该子进程应该退出.请指出我的代码中的错误.
Now, the above code returns only after deadline_timer
expires. What I want is that the child process should exit if it finishes before the timer expires or it (along with all the child processes it forks), should be terminated. Please point out the mistake in my code.
推荐答案
错误确实很简单:您应该取消截止日期计时器!
The mistake is indeed very simple: you should cancel the deadline timer!
io_service::run()
不会返回
- 处理程序产生的异常
- 没有更多的工作排队.
在进行最后期限计时器时,这意味着不满足第二个条件.因此io_service::run()
等待它是因为您要求这样做.
While the dead line timer is in progress, that means the second condition isn't met. So io_service::run()
waits for it because you asked it to.
其他说明:
- 使用错误代码来检测计时器取消,而不是无用时间比较
- 不需要循环链接计时器(实际上,这是在io_service永远无法完成的错误中进行询问)
- 您的代码未能初始化
stopped
和killed
- use the error code to detect timer cancellation instead of racy time comparisons
- no need to loop-chain the timer (in fact, that's asking for bugs where the io_service never completes)
- your code failed to initialize
stopped
andkilled
在Coliru上直播
Live On Coliru
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
namespace bp = boost::process;
class Process {
public:
Process(std::string &cmd, const int timeout);
void run();
private:
void timeout_handler(boost::system::error_code ec);
const std::string command;
const int timeout;
bool killed = false;
bool stopped = false;
std::string stdOut;
std::string stdErr;
int returnStatus = 0;
boost::asio::io_service ios;
boost::process::group group;
boost::asio::deadline_timer deadline_timer;
};
Process::Process(std::string &cmd, const int timeout) : command(cmd), timeout(timeout), deadline_timer(ios) {}
void Process::timeout_handler(boost::system::error_code ec) {
if (stopped)
return;
if (ec == boost::asio::error::operation_aborted)
return;
if (deadline_timer.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
std::cout << "Time Up!" << std::endl;
group.terminate(); // NOTE: anticipate errors
std::cout << "Killed the process and all its decendants" << std::endl;
killed = true;
stopped = true;
deadline_timer.expires_at(boost::posix_time::pos_infin);
}
//NOTE: don't make it a loop
//deadline_timer.async_wait(boost::bind(&Process::timeout_handler, this, boost::asio::placeholders::error));
}
void Process::run() {
std::future<std::string> dataOut;
std::future<std::string> dataErr;
deadline_timer.expires_from_now(boost::posix_time::seconds(timeout));
deadline_timer.async_wait(boost::bind(&Process::timeout_handler, this, boost::asio::placeholders::error));
bp::child c(command, bp::std_in.close(), bp::std_out > dataOut, bp::std_err > dataErr, ios, group,
bp::on_exit([=](int e, std::error_code ec) {
// TODO handle errors
std::cout << "on_exit: " << ec.message() << " -> " << e << std::endl;
deadline_timer.cancel();
returnStatus = e;
}));
ios.run();
stdOut = dataOut.get();
stdErr = dataErr.get();
c.wait();
returnStatus = c.exit_code();
}
int main(int argc, char **argv) {
if (argc < 2) {
std::cout << "Usage: \na.out <command>" << std::endl;
exit(1);
}
std::vector<std::string> arguments(argv + 1, argv + argc);
std::string command;
for (const auto &tok : arguments) {
command += tok + " ";
}
std::cout << command << std::endl;
Process p(command, 2);
p.run();
}
打印例如
$ ./sotest 'echo hello'
echo hello
on_exit: Success -> 0
$ ./sotest 'sleep 1'
sleep 1
on_exit: Success -> 0
$ ./sotest 'sleep 3'
sleep 3
Time Up!
Killed the process and all its decendants
on_exit: Success -> 9
这篇关于在超时模式下以异步方式运行使用Boost进程的进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!