Implementatie van een klasse die een onderliggend proces met time-out uitvoert met behulp van het boostproces in de async-modus

In de volgende code probeer ik een programma te implementeren dat een proces en retourneert zijn retourcode, stdout en stderr. Het heeft ook een concept van een time-out waarna het onderliggende proces wordt beëindigd. Deze klasse zal worden gebruikt als onderdeel van een multi-threaded applicatie.

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

Hieronder staat de code die ik gebruik om het te testen

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

Geef uw waardevolle opmerkingen en suggesties.

Reacties

  • Welkom bij Code Review! U zegt dat u ' probeert het programma te implementeren. Werkt het tot nu toe? Maakt u zich zorgen over uw code? Het ' is ok als je ' t niet gebruikt, zolang de code werkt zoals bedoeld.
  • @Mast De code werkt voor de gevallen waarvoor ik hem heb getest. Maar ik vraag me af of er race-omstandigheden en andere bugs zijn?
  • Goed! Kun je die tests opnemen?

Antwoord

Ik “moet nog steeds wennen aan Boost::Process, dus misschien heb ik dit verkeerd. Heb je de c.wait() echt nodig na ios.run()?

Volgens hun documentatie , de controle wordt geblokkeerd op ios.run() totdat io_context is voltooid of natuurlijk als uw deadline_timer verloopt.

Antwoord

Ontbrekende tests

I zie geen tests voor commandos die een time-out zouden moeten hebben. Een duidelijk voorbeeld zou sleep 2 kunnen zijn, als we het uitvoeren met een time-out van 1. Een meer rigoureuze test zou een commando zijn dat alle signalen negeert (van degene die genegeerd kunnen worden) – we moeten ervoor zorgen dat als SIGTERM niet werkt, het SIGKILL uiteindelijk.

Datagestuurde tests

Ik hou er niet van om loops te zien in testgevallen. Heeft Boost Test voorzieningen voor herhalen van tests met verschillende gegevens? De meeste test frameworks doen; Het zou me verbazen als dat m afkomstig van Boost.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *