Implementarea unei clase care rulează un proces copil cu expirare folosind procesul boost în modul asincron

În următorul cod, încerc să implementez un program care rulează un procesează și returnează codul său de returnare, stdout și stderr. De asemenea, are un concept de timeout după care procesul copilului este ucis. Această clasă va fi utilizată ca parte a unei aplicații cu mai multe fire.

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

Mai jos este codul pe care îl folosesc pentru a-l testa

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

Vă rugăm să furnizați comentariile și sugestiile dvs. valoroase.

Comentarii

  • Bun venit la Code Review! Spui că ' încerci să implementezi programul. Funcționează până acum? Aveți vreo îngrijorare cu privire la codul dvs.? ' este ok dacă nu ' t, atâta timp cât codul funcționează conform intenției.
  • @Mast Codul funcționează pentru cazurile pentru care l-am testat. Dar mă întreb dacă există condiții de cursă și alte erori?
  • Bine! Ați putea include aceste teste?

Răspuns

Încă mă obișnuiesc cu Boost::Process, deci poate că am greșit asta. Cu toate acestea, chiar aveți nevoie de c.wait() după ios.run()?

Conform documentație , controlul va fi blocat la ios.run() până când io_context este terminat sau, desigur, dacă deadline_timer expiră.

Răspuns

Testele lipsă

I Nu vedeți niciun test pentru comenzi care ar trebui să expire. Un exemplu evident ar putea fi sleep 2, dacă îl executăm cu un timeout 1. Un test mai riguros ar fi o comandă care ignoră toate semnalele (dintre cele care pot fi ignorate) – ar trebui să ne asigurăm că, dacă SIGTERM nu funcționează, va primi SIGKILL în cele din urmă.

Testele bazate pe date

Nu-mi place să văd bucle în cazurile de testare. Testul Boost are dispoziții pentru repetarea testelor cu date diferite? Majoritatea cadrelor de testare o fac; aș fi surprins dacă ar fi m provenind de la Boost.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *