Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Boost::Asio socket async and sync operations together

I'm a Boost C++ newbie and, using it to write a Server-like application I am wondering if it is possible to concurrently use boost::asio::ip::tcp::socket::async_read_some(...) and boost::asio::ip::tcp::socket::write_some(...).

In my scenario a Connection object listens continuously via:

    void Connection::doRead()
{
    auto self(shared_from_this());
    socket_.async_read_some(boost::asio::buffer(data_rx_, max_length),
        [this, self](boost::system::error_code ec, std::size_t length)
        {
          if (!ec)
          {
              afterReading(length);
              doRead();
          }
        });
}

At the same time, an asynchronous function callback (running in a different thread) could invoke socket_.read_write while Connection is "reading".

I've read various Boost::Asio docs but this scenario was never covered.

Is this allowed? What should be done to avoid it if not?

EDIT:

I have read, as suggested, various answers including this: Why do I need strand per connection when using boost::asio?, but still can't find an answer as it is not specified wether mixing async and sync (called by different threads) calls is safe or not.

like image 968
Francis Straccia Avatar asked Sep 26 '22 01:09

Francis Straccia


1 Answers

It is not safe.

This answer goes into details, but to summarize, it is not safe for multiple threads to make calls concurrently, with the exception that multiple synchronous calls may be safe if supported by the OS. If one thread initiates an asynchronous operation on a socket while another thread is performing a synchronous operation on the same socket, then it falls into the former case and is not safe.

The most elegant solution is to avoid mixing synchronous and asynchronous operations.

  • If using synchronous operations and the OS does not support concurrent synchronous operations, then perform explicit locking, such as by using mutexes.
  • If using asynchronous operations and multiple threads, then use an explicit strand to prevent concurrent invocation of handlers.

Also, one can synchronously block waiting for an asynchronous operation to complete by leveraging Boost.Asio's support for futures. This approach can allow for one to expose a synchronous blocking API to a user, but use asynchronous operations internally. The initiating operation (async_*) would need to be invoked within a strand, and if the caller is not running within the context of the strand, then some form of synchronization will need to be used to allow the caller to wait for Asio to create the future object.

like image 142
Tanner Sansbury Avatar answered Sep 29 '22 05:09

Tanner Sansbury