C++ how to run 2 boost:asio: io_context at the same time

Viewed 1586

I have 2 boost::asio::io_context variables, one for the connection between my Raspberry Pi and my arduino and the other one for the connection between the Raspberry Pi and the client through a tcp server. I have tested both connections separately and they work perfectly fine. However, when I try to join them, it doesn't work. I understand that by making two io.run, the second will never be entered as the program will be stuck in the first one, so how can I insert the io.run inside the io_context.run()?

My main is the following:

boost::asio::io_context io_context;
boost::asio::io_context io;
int main{
  //create a server object to accept incoming client connections
  tcp_server server(io_context);

  open_port_arduino();
  
  start = std::chrono::high_resolution_clock::now(); //time_init
  //writes to arduino with intervals of 2 seconds
  tim.expires_after(boost::asio::chrono::seconds {2});
  tim.async_wait(timer_handler);
  //reads from arduino
  start_read_arduino();

  io.run();
  io_context.run();
}
1 Answers

To reply to your comment: you do not need a second io_context.

However, you can (see also Do we need multiple io_service per thread for threaded boost::asio server with a single acceptor and https://theboostcpplibraries.com/boost.asio-scalability).

One benefit could be thread isolation: if you run separate contexts on separate threads, there is implicit task serialization. (Contrast with a single context on multiple threads, where tasks might require manual serialization e.g. using strands).

The most straight forward thing I'd imagine for you to "fix" it is to simply pass a reference to the same io_service to all parties:

Live On Coliru

static io_context io;

struct tcp_server {
    tcp_server(io_context& ctx) : _ctx(ctx) {}
  private:
    io_context& _ctx; // REFERENCE
};

int main() {
    tcp_server server(io);

    // ...

(The live demo prints "Timer callback: success" as expected)

Multi-Thread

If some of your IO tasks block for significant amounts of time you will want to run several threads. In that case I'd switch the approach to passing an executor to your classes, and make sure they wrap it in strands:

boost::asio::thread_pool io(2); // two threads
using boost::asio::any_io_executor;

struct tcp_server {
    tcp_server(any_io_executor ex) : _ex(make_strand(ex)) {}
  private:
    any_io_executor _ex;
};

And then

int main() {
    //create a server object to accept incoming client connections
    tcp_server server(io.get_executor());

Again, the Live demo prints "Timer callback: Success" as expected.

Related