1

I am writing an application using boost.asio. I've an object of type boost::asio::ip::tcp::socket and (of course) I've boost::asio::io_context which run's function was called from only one thread. For writing data to the socket there are a couple of ways but currently I use socket's function async_write_some, something like the code below:

void tcp_connection::write(packet_ptr packet)
{
    m_socket.async_write_some(boost::asio::buffer(packet->data(), packet->size()),
                              std::bind(&tcp_connection::on_write, this, std::placeholders::_1, std::placeholders::_2, packet));
}

There is another function in boost::asio namespace - async_write. And the documentation of async_write says:

This operation is implemented in terms of zero or more calls to the stream's async_write_some function, and is known as a composed operation. The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.

In async_write_some's documentation there is no such kind of 'caution'.

That's a little bit confusing to me and here I've got the following questions:

  1. Is it safe to call async_write_some without waiting for the previous call to be finished? As far as I understood from boost's documentation I shouldn't do that with async_write, but what about async_write_some?
  2. If yes, is the order in which the data is written to the socket the same as the functions were called? I mean if I called async_write_some(packet1) and async_write_some(packet2) - are the packets going to be written to the socket in the same order?
  3. Which function I should use? What is the difference between them?
  4. What is the reason that it's not safe to call async_write while the previous one hasn't finished yet?
Arman Oganesyan
  • 396
  • 3
  • 11

1 Answers1

3
  1. no; the reason for that is probably documented with the underlying sockets API (BSD/WinSock).

  2. not applicable. Note that the order in which handlers are invoked is guaranteed to match the order in which they were posted, so you could solve it using an async chain of async_write_some calls where the completion handler posts the next write. This is known as an implicit strand (see https://www.boost.org/doc/libs/master/doc/html/boost_asio/overview/core/async.html and Why do I need strand per connection when using boost::asio?).

  3. 99% of the time, use the free function. The difference is that it implements composed operation to send a "unit" of information, i.e. an entire buffer, message, or until a given completion condition is met.

    async_write_some is the lowest-level building block, which doesn't even guarantee to write all of the data: remarks:

    The write operation may not transmit all of the data to the peer. Consider using the async_write function if you need to ensure that all data is written before the asynchronous operation completes.

  4. It's not unsafe¹ in the strictest sense. It just will not lead to correct results: this is because the order in which handlers are invoked leads to data being written to the socket in mixed-up order.


¹(unless you access the shared IO objects concurrently without synchronization)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thank you for your response. boost::asio::io_context guarantees that my handlers will be executed only by the thread(s) that called run function. But it is true only for handlers, isn't it? Am I correct that boost::asio::io_context may execute both write operations at the same time? – Arman Oganesyan Jun 15 '21 at 21:05
  • Not since you run it on only one thread. Otherwise, yes, handlers may run concurrently and you have to make provisions (e.g. using a strand) – sehe Jun 15 '21 at 21:39
  • Thanks, I guess it's clear for me how handlers are being called. But could you please clarify - even in case if I call run from only one thread, asynchronous tasks may be executed on different threads? If I called async_write twice, io_context may execute both of them on two different threads and that's the reason why I should synchronize them? – Arman Oganesyan Jun 15 '21 at 21:57
  • No, they're run asynchronously, usually using library/kernel level async interfaces. In practice this means that the completions of (sub) operations may be interleaving. It's not so much "synchronizing" as "serializing (execution)" or "sequentializing". – sehe Jun 16 '21 at 13:20
  • In short, though concurrency and asynchrony are orthogonal concepts, they share some of the complicating concerns - for subtly different reasons. – sehe Jun 16 '21 at 13:21