Implementering av en klasse som kjører en underordnet prosess med timeout ved hjelp av boost-prosess i asynkroniseringsmodus

I den følgende koden prøver jeg å implementere et program som kjører en prosess og returnerer returkoden, stdout og stderr. Det har også et begrep om tidsavbrudd, hvoretter barneprosessen blir drept. Denne klassen vil bli brukt som en del av et flertrådet program.

#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 koden jeg bruker for å 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 } } 

Gi dine verdifulle kommentarer og forslag.

Kommentarer

  • Velkommen til Code Review! Du sier at du ' prøver å implementere programmet. Fungerer det så langt? Har du noen bekymringer om koden din? Det ' er ok hvis du ikke ' t, så lenge koden fungerer som forutsatt.
  • @Mast Koden fungerer for de sakene jeg har testet den for. Men jeg lurer på om det er noen løpsforhold og andre bugs?
  • Bra! Kan du inkludere disse testene?

Svar

Jeg blir fortsatt vant til Boost::Process, så kanskje jeg har misforstått dette. Trenger du imidlertid virkelig c.wait() etter ios.run()?

I henhold til dokumentasjon , kontroll blir blokkert på ios.run() til io_context er ferdig eller selvfølgelig hvis din deadline_timer utløper.

Svar

Manglende tester

I ikke se noen tester for kommandoer som skulle gå ut. Et åpenbart eksempel kan være sleep 2, hvis vi kjører det med en tidsavbrudd på 1. En strengere test ville være en kommando som ignorerer alle signaler (av de som kan ignoreres) – vi bør sørge for at hvis SIGTERM ikke fungerer, at det vil få SIGKILL til slutt.

Datadrevne tester

Jeg liker ikke å se sløyfer i testtilfeller. Har Boost Test bestemmelser om gjentar tester med forskjellige data? De fleste testrammer gjør det. Jeg vil bli overrasket om det var m utstedt fra Boost.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *