I've slightly changed the example found here to run all processes from a single io_context object which is defined in main, and delivered to each task, runs from a different thread.
I was expecting all tasks to complete successfully since they should take less than the timeout. However, I'm getting timeout on all the tasks. Any idea why ?
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
using duration = std::chrono::system_clock::duration;
namespace asio = boost::asio;
using namespace std::chrono_literals;
std::string ExecuteProcess(boost::filesystem::path exe,
std::vector<std::string> args, //
duration time, //
std::error_code& ec,
asio::io_context& ioc) {
namespace bp = boost::process;
std::future<std::string> data, err_output;
bp::group g;
bp::child child(exe, args, ioc, g, bp::error(ec),
bp::std_in.null(), //
bp::std_out > data, //
bp::std_err > err_output);
if (std::error_code ignore; child.running()) {
g.terminate(ignore);
}
if (data.wait_for(time) == std::future_status::ready) {
ec.clear();
return data.get();
}
ec = make_error_code(asio::error::timed_out);
return {};
}
int main() {
constexpr duration timeout = 20ms;
constexpr auto script2 = "sleep 0.00005; echo -n 'Hello world'";
constexpr auto script1 = "/usr/bin/curl http://httpbin.org/ip -m 5";
asio::io_context ioc;
boost::asio::detail::thread_group collect_threads;
for (int i = 0 ; i < 20 ; i++) {
collect_threads.create_thread([&]() {
std::error_code ec_;
auto s = ExecuteProcess("/bin/bash", {"-c", script2}, timeout, ec_, ioc);
std::cout << "got " << ec_.message() << ": " << s << std::endl;
});
}
ioc.run();
}
P.S:
For the timeout implementation, I was also considering the option to use result = waitpid(pid, &status, WNOHANG)
instead of std::future
.
However, when I've finished waiting and the process stdout seems correct, the check child.exit_code()
return 383 which means that the process still running - not sure why.
It took me forever to see, but... you actively terminate the child, before waiting for any result ¯\(ツ)/¯
With some re-ordering, and other minor tweaks:
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
using duration = std::chrono::system_clock::duration;
namespace asio = boost::asio;
using namespace std::chrono_literals;
std::string ExecuteProcess(boost::filesystem::path exe,
std::vector<std::string> args, //
duration time, //
std::error_code& ec, //
asio::io_context& ioc) {
namespace bp = boost::process;
std::future<std::string> data, err_output;
auto const deadline = std::chrono::steady_clock::now() + time;
bp::group g;
ec.clear();
bp::child child(exe, args, ioc, g, bp::error(ec), bp::std_in.null(), bp::std_out > data,
bp::std_err > err_output);
if (ec)
return {};
if (data.wait_until(deadline) == std::future_status::ready)
return data.get();
if (std::error_code ignore; child.running(ignore))
g.terminate(ignore);
ec = make_error_code(asio::error::timed_out); // TODO FIXME
return {};
}
int main() {
constexpr duration timeout = 20ms;
[[maybe_unused]] constexpr auto script1 = "/usr/bin/curl http://httpbin.org/ip -m 5";
[[maybe_unused]] constexpr auto script2 = R"(delay="0.0$RANDOM"; sleep "$delay"; echo -n "sanity restored after $delay")";
asio::io_context ioc;
auto work = make_work_guard(ioc); // prevent running out of work
std::thread io_thread([&ioc] { ioc.run(); });
for (int i = 0; i < 20; i++) {
std::error_code ec;
auto s = ExecuteProcess("/bin/bash", {"-c", script2}, timeout, ec, ioc);
std::cout << "got " << ec.message() << ": " << quoted(s) << std::endl;
}
work.reset(); // allow running out of work
io_thread.join();
}
Prints e.g.