Implementering af en klasse, der kører en underordnet proces med timeout ved hjælp af boost-processen i async-tilstand

I den følgende kode forsøger jeg at implementere et program, der kører en proces og returnerer sin returkode, stdout og stderr. Det har også et begreb om timeout, hvorefter barneprocessen dræbes. Denne klasse vil blive brugt som en del af en multi-threaded applikation.

#ifndef PROCESS_H_ #define PROCESS_H_ //Header #include <chrono> #include <thread> #include <iostream> #include <boost/process.hpp> #include <boost/asio.hpp namespace bp = boost::process; class Proces { public: Process(const std::string& cmd, const int timeout); void run(); int getReturnStatus(); //Must be only called after run() exits std::string getStdOut(); //Must be only called after run() exits std::string getStdErr(); //Must be only called after run() exits bool wasKilled(); //Must be only called after run() exits private: void initLog(); void timeoutHandler(const boost::system::error_code& ec); void kill(); const std::string command; const int timeout; int returnStatus; std::string stdOut; std::string stdErr; bool killed; bool stopped; boost::process::group group; boost::asio::io_context ioc; boost::asio::deadline_timer deadline_timer; }; #endif //Source #include "Process.h" Process::Process(const std::string& cmd, const int timeout): command(cmd), timeout(timeout), returnStatus(0), stdOut(""), stdErr(""), killed(false), stopped(false), ioc(), deadline_timer(ioc) { } void Process::timeoutHandler(const boost::system::error_code& ec) { if (stopped || ec == boost::asio::error::operation_aborted) { return; } std::cout << "Time Up!"<< std::endl; kill(); deadline_timer.expires_at(boost::posix_time::pos_infin); } void Process::run() { std::future<std::string> dataOut; std::future<std::string> dataErr; std::cout << "Running command: " << command << std::endl; bp::child c(command, bp::std_in.close(), bp::std_out > dataOut, bp::std_err > dataErr, ioc, group, bp::on_exit([=](int e, const std::error_code& ec) { std::cout << "on_exit: " << ec.message() << " -> " << e << std::endl; deadline_timer.cancel(); returnStatus = e; })); deadline_timer.expires_from_now(boost::posix_time::seconds(timeout)); deadline_timer.async_wait(std::bind(&Process::timeoutHandler, this, std::placeholders::_1)); ioc.run(); c.wait(); stdOut = dataOut.get(); stdErr = dataErr.get(); } //Must be only called after run() exits int Process::getReturnStatus() { return returnStatus; } //Must be only called after run() exits std::string Process::getStdOut() { return stdOut; } //Must be only called after run() exits std::string Process::getStdErr() { return stdErr; } void Process::kill() { std::error_code ec; group.terminate(ec); if(ec) { std::cout << "Error occurred while trying to kill the process: " << ec.message(); throw std::runtime_error(ec.message()); } std::cout << "Killed the process and all its descendants" << std::endl; killed = true; stopped = true; } //Must be only called after run() exits bool Process::wasKilled() { return killed; } 

Nedenfor er den kode, jeg bruger til at teste den

#define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE ProcessTest #include <boost/test/unit_test.hpp> #include "../src/Process.h" BOOST_AUTO_TEST_CASE( ProcessTest ) { const std::vector<std::string> commands = { "ls" , "pwd" , "uname -a" , "cat /proc/cpuinfo" , "wget https://dl.google.com/dl/earth/client/current/google-earth-pro-stable-current.x86_64.rpm"}; for(const auto& cmd: commands) { Process p(cmd, 3600); p.run(); BOOST_CHECK( p.getReturnStatus() == 0); // #1 continues on error } const std::vector<std::string> commandsThatShouldFail = { "ls doesnotexit" , "cat /proc/doesnotexist" , "wget https://dl.google.com/dl/earth/client/current/doesnotxist.rpm"}; for(const auto& cmd: commandsThatShouldFail) { Process p(cmd, 3600); p.run(); BOOST_CHECK( p.getReturnStatus() != 0); // #1 continues on error } } 

Giv dine værdifulde kommentarer og forslag.

Kommentarer

  • Velkommen til Code Review! Du siger, at du ' forsøger at implementere programmet. Fungerer det indtil videre? Har du nogen bekymringer omkring din kode? Det ' er ok, hvis du ikke ' t, så længe koden fungerer som beregnet.
  • @Mast Koden fungerer i de tilfælde, hvor jeg har testet den. Men jeg spekulerer på, om der er nogen raceforhold og andre fejl?
  • Godt! Kunne du medtage disse tests?

Svar

Jeg vænner mig stadig til Boost::Process, så måske har jeg forkert det. Har du dog brug for c.wait() efter ios.run()?

I henhold til deres dokumentation , kontrol blokeres ved ios.run() indtil io_context er færdig eller selvfølgelig, hvis din deadline_timer udløber.

Svar

Manglende tests

I kan ikke se nogen tests for kommandoer, der skal timeout. Et indlysende eksempel kan være sleep 2, hvis vi kører det med en timeout på 1SIGTERM ikke virker, at det bliver SIGKILL i sidste ende.

Datadrevne tests

Jeg kan ikke lide at se sløjfer inden for testsager. Har Boost Test mulighed for gentage tests med forskellige data? De fleste testrammer gør det; jeg ville blive overrasket, hvis det var m udstedes fra Boost.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *