Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement async/await syntax properly using Boost.Asio

Tags:

c++

boost-asio

I'm trying to implement some network application using Boost.Asio. I have a problem with multiple layers of callbacks. In other languages that natively support async/await syntax, I can write my logic like this

void do_send(args...) {
  if (!endpoint_resolved) {
    await resolve_async(...);  // results are stored in member variables
  }
  if (!connected) {
    await connect_async(...);
  }
  await send_async(...);
  await receive_async(...);
}

Right now I have to write it using multiple layers of callbacks

void do_send(args...) {
  if (!endpoint_resolved) {
    resolve_async(..., [captures...](args...) {
      if (!connected) {
        connect_async(..., [captures...](args...) {
          send_async(..., [captures...](args...) {
            receive_async(..., [captures...](args...) {
              // do something
            });  // receive_async
          });  // send_async
        });  // connect_async
      }
    });
  }
}

This is cumbersome and error-prone. An alternative is to use std::bind to bind member functions as callbacks, but this does not solve the problem because either way I have to write complicated logic in the callbacks to determine what to do next.

I'm wondering if there are better solutions. Ideally I would like to write code in a synchronous way while I can await asynchronously on any I/O operations.

I've also checked std::async, std::future, etc. But they don't seem to fit into my situation.

like image 646
kebugcheck Avatar asked Mar 17 '26 02:03

kebugcheck


1 Answers

Boost.Asio's stackful coroutines would provide a good solution. Stackful coroutines allow for asynchronous code to be written in a manner that reads synchronous. One can create a stackful coroutine via the spawn function. Within the coroutine, passing the yield_context as a handler to an asyncornous operation will start the operation and suspend the coroutine. The coroutine will be resumed automatically when the asynchronous operation completes. Here is the example from the documentation:

boost::asio::spawn(my_strand, do_echo);

// ...

void do_echo(boost::asio::yield_context yield)
{
  try
  {
    char data[128];
    for (;;)
    {
      std::size_t length =
        my_socket.async_read_some(
          boost::asio::buffer(data), yield);

      boost::asio::async_write(my_socket,
          boost::asio::buffer(data, length), yield);
    }
  }
  catch (std::exception& e)
  {
    // ...
  }
}
like image 178
Tanner Sansbury Avatar answered Mar 19 '26 15:03

Tanner Sansbury