在超时模式下以异步方式运行使用Boost进程的进程 [英] Running a process using boost process in async mode with timeout

查看:105
本文介绍了在超时模式下以异步方式运行使用Boost进程的进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,我试图实现一个运行shell命令并获取stdiostderr并返回代码的程序.我正在按照建议

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()不会返回

  1. 处理程序产生的异常
  2. 没有更多的工作排队.

在进行最后期限计时器时,这意味着不满足第二个条件.因此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永远无法完成的错误中进行询问)
  • 您的代码未能初始化stoppedkilled
  • 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 and killed

在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆