1

I am using boost::process::child and boost::process::async_pipe to start an application and read asynchronously (through the means of boost::asio) everything that app outputs on screen whenever this happens.

I want to check also if the application is alive by using child::running() method; if not running I'd like to read the exit code using child::exit_code.

This is very useful ESPECIALLY as it is a way to be notified about an application crashing or exiting unexpectedly (I could not find a better way); when the app exits the callback is called with boost::system::error_code set.

Do you know if I can use these two methods inside the callback called by async_pipe::async_read_some ?

In general the much more simple question would be if child::running() and child::exit_code() are thread safe (in both Windows and Linux).

 namespace bp = boost::process;

 char my_buffer[1024];

 boost::asio::io_service io;
 bp::async_pipe in_pipe(io);

 void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);

 void schedule_read()  {
     in_pipe.async_read_some(
         boost::asio::buffer(my_buffer),
         boost::bind(&handle_pipe_read, 
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
 }

 void handle_pipe_read(
    const boost::system::error_code &ec, 
    std::size_t bytes_transferred
    )
 {
     // Q: Is this call possible? 'handle_pipe_read' can run in any thread
     if(c->running())
        std::cout << "I am alive" << std::endl;
     else
        std::cout << "EXIT CODE:" << c->exit_code() << std::endl;

     if(ec) return; //app probably exit

     // Do something with buffer and re-schedule

     schedule_read();
 }

 int main() {
   bp::child c("my_program_url", bp::std_out > in_pipe);
   any_c = &c;

   schedule_read();
   io.run();    
 }
Abruzzo Forte e Gentile
  • 14,423
  • 28
  • 99
  • 173

2 Answers2

1

Since you only run the io_service::run() on the main thread, all completion handlers also run there. There's no threading.

Remember to pass the io_service to the child, and use the on_exit handler:

Live On Coliru

#include <boost/process.hpp>
//#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <system_error>
#include <utility>
#include <iostream>

namespace bp = boost::process;

char my_buffer[1024];

boost::asio::io_service io;
bp::async_pipe in_pipe(io);

void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);

void schedule_read() {
    in_pipe.async_read_some(
        boost::asio::buffer(my_buffer),
        boost::bind(&handle_pipe_read, _1, _2));
}

void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred) {
    if (ec)
        return; // app probably exit

    // Do something with buffer and re-schedule
    std::cout.write(my_buffer, bytes_transferred);

    if (in_pipe.is_open())
        schedule_read();
}

int main() {
    bp::child c("/bin/ls", bp::std_out > in_pipe,
            bp::on_exit([](int code, std::error_code ec) {
                std::cout << "Child exited (" << code << "): " << ec.message() << std::endl;
                in_pipe.close();
            }), io);

    schedule_read();
    io.run();

    std::cout << "Service done (" << c.exit_code() << ")" << std::endl;
}

Prints:

a.out
main.cpp
Child exited (0): Success
Service done (0)
sehe
  • 374,641
  • 47
  • 450
  • 633
  • that's a lovely function I was not aware of. Let me check and I''ll come back to you. – Abruzzo Forte e Gentile Dec 05 '17 at 13:08
  • Got a question for you just in case: do you know where this boost::process::on_exit is executed? Is it a callback that can be executed in any thread as it happens for all boost::asio handlers ? – Abruzzo Forte e Gentile Dec 05 '17 at 13:19
  • After I test I can also see that if the child application crashes this handler is not called...so I am still not sure about what is the best way to be notified about a crash...Maybe using also wait() which throws an exception if the app is not running and it has not terminated correctly, – Abruzzo Forte e Gentile Dec 05 '17 at 13:23
  • I told you in the first sentence: it's a completion handler and is called on the service thread (the one that runs `io_service::run()`). If you have multiple threads running your service, then you don't know which one (so use strands and post the pipe-close back to the service) – sehe Dec 05 '17 at 13:23
  • @AbruzzoForteeGentile I expected `on_exit` be called http://www.boost.org/doc/libs/master/doc/html/boost/process/on_exit.html – sehe Dec 05 '17 at 13:24
  • I've used Boost Process and tested crash resilience, it was no problem. I posted my approach the other day: https://stackoverflow.com/questions/47630796/function-with-variable-parameter-size-how-to-conditionally-set-some-arguments/47634444#47634444 - I just tested a crashing child process on Linux and it was no problem. Is this windows-specific? – sehe Dec 05 '17 at 13:34
  • thanks for clarifying on asio completion handler. I did not spot that on your post. Unfortunately I am running it on LINUX and it is not called. I am using boost 1.65.1 In the child process I spawn I simply call std::throw(std::runtime_error("I am crashing app")). The only difference here is that I am using asio::io_service instead of asio::context. Let me have a look at your code...give me 5 mins. – Abruzzo Forte e Gentile Dec 05 '17 at 13:37
  • Nothing for whatever reason, it does not work for me. The only solution tha works for me it a. schedule a completion read b. check if error_code is set c. call child::running() – Abruzzo Forte e Gentile Dec 07 '17 at 18:33
0

The only solution that worked for me is the following

  1. schedule a completion read
  2. always check calling child::running()
  3. on error_code set don't reschedule

When the pipe gets broken (because of a crash) the completion handler for a read has the boost::error_code argument set to true. Despite this I've seen cases where child::running() is false also when boost::error_code is NOT set.

Abruzzo Forte e Gentile
  • 14,423
  • 28
  • 99
  • 173