Implementering av en klass som kör en underordnad process med timeout med boost-processen i async-läge

I följande kod försöker jag implementera ett program som kör en processen och returnerar sin returkod, stdout och stderr. Det har också ett begrepp om timeout, varefter barnprocessen dödas. Den här klassen kommer att användas som en del av en applikation med flera trådar.

#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; } 

Nedan följer koden jag använder för att testa 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 } } 

Ange dina värdefulla kommentarer och förslag.

Kommentarer

  • Välkommen till Code Review! Du säger att du ' försöker implementera programmet. Fungerar det hittills? Har du några problem med din kod? Det ' är ok om du inte ' t, så länge koden fungerar som avsett.
  • @Mast Koden fungerar för de fall jag har testat den för. Men jag undrar om det finns några tävlingsförhållanden och andra buggar?
  • Bra! Kan du inkludera dessa tester?

Svar

Jag vänjer mig fortfarande vid Boost::Process, så kanske jag har fel här. Behöver du verkligen c.wait() efter ios.run()?

Enligt deras dokumentation , kontroll kommer att blockeras vid ios.run() tills io_context är klar eller naturligtvis om din deadline_timer upphör att gälla.

Svar

Saknade tester

I se inte några tester för kommandon som ska ta slut. Ett uppenbart exempel kan vara sleep 2 om vi kör det med en timeout på 1. Ett strängare test skulle vara ett kommando som ignorerar alla signaler (av de som kan ignoreras) – vi bör se till att om SIGTERM inte fungerar, att det blir SIGKILL så småningom.

Datadrivna tester

Jag gillar inte att se loopar i testfall. Har Boost Test möjlighet att upprepa tester med olika data? De flesta testramar gör det. Jag skulle bli förvånad om det var m från Boost.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *