Implémentation dune classe qui exécute un processus enfant avec timeout à laide du processus boost en mode asynchrone

Dans le code suivant, jessaye dimplémenter un programme qui exécute un traite et renvoie son code de retour, stdout et stderr. Il a également un concept de délai dexpiration après lequel le processus enfant est tué. Cette classe sera utilisée dans le cadre dune application multithread.

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

Voici le code que jutilise pour le tester

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

Veuillez fournir vos précieux commentaires et suggestions.

Commentaires

  • Bienvenue dans Code Review! Vous dites que vous ' essayez de mettre en œuvre le programme. Cela fonctionne-t-il jusquà présent? Avez-vous des inquiétudes concernant votre code? Cela ' est ok si vous ne ' t, tant que le code fonctionne comme prévu.
  • @Mast Le code fonctionne pour les cas pour lesquels je lai testé. Mais je me demande sil y a des conditions de course et dautres bugs?
  • Bien! Pourriez-vous inclure ces tests?

Réponse

Je « suis toujours en train de mhabituer à Boost::Process, alors peut-être que je » me suis trompé. Cependant, avez-vous vraiment besoin du c.wait() après ios.run()?

Selon leur documentation , le contrôle sera bloqué à ios.run() jusquà ce que io_context soit terminé ou bien sûr si votre deadline_timer expire.

Réponse

Tests manquants

I ne voyez aucun test pour les commandes qui devraient expirer. Un exemple évident pourrait être sleep 2, si nous lexécutons avec un délai dexpiration de 1. Un test plus rigoureux serait une commande qui ignore tous les signaux (de ceux qui peuvent être ignorés) – nous devrions nous assurer que si SIGTERM ne fonctionne pas, il obtiendra SIGKILL éventuellement.

Tests basés sur les données

Je naime pas voir les boucles dans les cas de test. Boost Test a-t-il prévu répéter des tests avec des données différentes? La plupart des frameworks de test le font; je serais surpris si c’était m provenant de Boost.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *