Запуск процесса с использованием процесса повышения в асинхронном режиме с таймаутом - PullRequest
0 голосов
/ 03 октября 2018

В следующем коде я пытаюсь реализовать программу, которая запускает команду оболочки и получает stdio, stderr и код возврата.Я делаю это, используя boost process в режиме async, как рекомендовано здесь .

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 Ответ

0 голосов
/ 03 октября 2018

Ошибка действительно очень проста: вы должны отменить таймер крайнего срока!

io_service::run() не вернется, если только

  1. исключение, исходящее от обработчика
  2. больше работы не ставится в очередь.

Пока работает таймер мертвой линии, это означает, что второе условие не выполнено.Так что io_service::run() ждет его, потому что вы попросили об этом.

Другие примечания:

  • используйте код ошибки для обнаружения отмены таймера вместо сравнений с быстрым временем
  • нет необходимости зацикливать таймер (на самом деле, он запрашивает ошибки, когда io_service никогда не завершается)
  • ваш код не смог инициализировать stopped и killed

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 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...