Ошибка действительно очень проста: вы должны отменить таймер крайнего срока!
io_service::run()
не вернется, если только
- исключение, исходящее от обработчика
- больше работы не ставится в очередь.
Пока работает таймер мертвой линии, это означает, что второе условие не выполнено.Так что 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