代码之家  ›  专栏  ›  技术社区  ›  bisarch

使用带有超时的异步模式下的boost进程运行进程

  •  2
  • bisarch  · 技术社区  · 6 年前

    在下面的代码中,我尝试实现一个运行shell命令并获取 stdio , stderr 返回代码。我用的是 boost process async 模式建议 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 到期。我想要的是,如果子进程在计时器到期之前完成,它应该退出,或者它(连同它分叉的所有子进程)应该终止。请指出我的代码中的错误。

    1 回复  |  直到 6 年前
        1
  •  2
  •   sehe    6 年前

    错误确实很简单:你应该取消最后期限计时器!

    io_service::run() 不会回来,除非

    1. 由处理程序发出的异常
    2. 没有更多的工作排队。

    当死线计时器正在进行时,这意味着第二个条件不满足。所以 IO_服务::运行() 等待它,因为你要求它。

    其他注释:

    • 使用错误代码检测计时器取消,而不是Racy时间比较
    • 不需要循环链接计时器(事实上,这是在IO服务从未完成的地方请求错误)
    • 代码初始化失败 stopped killed

    科里鲁生活

    #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 decendents" << 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 decendents
    on_exit: Success -> 9