Suppose when calling io_service::run(), there are multiple async_read operations scheduled (There may be other operations in between them). What happens when an asynchronous operation like async_write is scheduled in the ReadHandler-function?
void handler(const boost::system::error_code& error, std::size_t bytes) {
async_write(sock, boost::asio::buffer(wbuf), whandler);
}
That is, when will the async_write be invoked?
I would expect the order of execution to be:
1) async_read //1
2) async_write
3) async_read //2
4) async_write
Is this order of execution guaranteed?
No, it's not guaranteed. For example, if the first handler is invoked and wants to write, what if the second buffer is not yet available to read? Of course the write should come first. But what if the write is not possible by the time the second buffer is ready to read? Then of course the second read should occur before the first write.
You can force the order of execution using strands
http://www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/io_service__strand.html
An excellent description is here: Why do I need strand per connection when using boost::asio?
You are misusing the boost::asio interface.
There may not be more than one pending read operations on a single socket.
Quote from the boost::asio::async_read docs:
This operation is implemented in terms of zero or more calls to the
stream's async_read_some function, and is known as a composed
operation. The program must ensure that the stream performs no other
read operations (such as async_read, the stream's async_read_some
function, or any other composed operations that perform reads) until
this operation completes.
Related
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:
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?
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?
Which function I should use? What is the difference between them?
What is the reason that it's not safe to call async_write while the previous one hasn't finished yet?
no; the reason for that is probably documented with the underlying sockets API (BSD/WinSock).
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?).
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.
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)
io_service::run() is called by thread A. Is it safe to call async_write from thread B?
io_service::run() is called by thread A. Are async operations executed by thread A, or is thread A only guaranteed to call handlers and behind the scenes there could be additional threads that execute the operations?
io_service::run() is called by thread A. Some thread calls async_read and async_write using the same buffer. Is it safe to assume that the buffer will be accessed by at most one operation at a time? Or is it so that only handlers are called serially, but behind the scenes reads and writes can occur simultaneously?
The documentation says "The program must ensure that the stream performs no other read operations (such as async_read, the stream's async_read_some function, or any other composed operations that perform reads) until this operation completes.". Is it correct to interpret this as "You must not perform more than one read operation on a socket at a time. But you may perform 10 read operations on 10 distinct sockets."?
Having a socket that indefinitely accepts data, is it a good idea to call async_read and call it again from async_read's handler?
Does io_service::stop() stop all pending async operations or simply stops accepting new ones and executes the pending ones?
Yes, providing the io_service is tied to whatever is calling async_write. However, it should be noted that it is safe to call async_write from thread B even if the run is not called: it'll get queued in the io_service and wait until one of the run-ing calls are completed.
The callbacks posted to the io_service will run on thread A. Other async operations (such as timer operations) can happen on other threads. What is guarenteed to be on A and what is on its own thread is defined by the specific object being used, not by io_service.
Nope. Yup-ish. Depends on the class calling io_service.
Yes.
Yes, in fact this is super common, as it both ensures that only 1 async_read call is running at a time for a given socket and that there is always "work" for the io_service.
It usually finished the last callback and then stops accepting new ones and stops processing pending ones. It actually still accepts new ones but forces a reset is called before any other callbacks are called.
io_service is a message queue (basically), while a socket that posts its messages to the io_service is something else entirely.
1: Yes
4: Yes, it's okay to perform distinct operations on distinct sockets.
5: Yes, if you check the examples that's how they do it.
6: Considering the reference manual says
All invocations of its run() or run_one() member functions should return as soon as possible.
I would say it might do any.
For number 2 and 6, the source is available so the best way to answer those question is by downloading and reading it.
I am continuously reading from a socket with async_read(). However on some events I have to send data through the same socket synchronously and wait for an ACK (also synchronously) in an event handler other than the above mentioned async_read's. I am waiting for the ACK in a synchronous read() call. (Please not that I am not talking about async_read_some and read_some).
Is it OK to call a sync read() while an async_read() is pending in the background?
Is it possible that async_read() already received half a message into its internal buffer and my sync read() will return with the second half?
How can I gracefully cancel/suspend async_read() (without any data loss), so I can safely call sync read() in the meantime?
You may not do that.
Quote from the boost doc:
This operation is implemented in terms of zero or more calls to the stream's async_read_some function, and is known as a composed operation. The program must ensure that the stream performs no other read operations (such as async_read, the stream's async_read_some function, or any other composed operations that perform reads) until this operation completes.
As plain boost::asio::read is also a composed read operation this might invoke UB.
To gracefully stop your async_read you can call cancel(*) nevertheless should think about your design when mixing async and normal operations. I would recommend sending the ACK from the async_read-callback handler.
(*) Note that cancel has some disadvantages, described in the link. One is e.g. that `cancel might be ignored.
I'm sending data asynchronously to TCP socket. Is it valid to send the next data piece before the previous one was reported as sent by completion handler?
As I know it's not allowed when sending is done from different threads. In my case all sending are done from the same thread.
Different modules of my client send data to the same socket. E.g. module1 sent some data and will continue when corresponding completion handler is invoked. Before this io_service invoked deadline_timer handler of module2 which leads to another async_write call. Should I expect any problems here?
Is it valid to send the next data piece before the previous one was
reported as sent by completion handler?
No it is not valid to interleave write operations. This is very clear in the documentation
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.
emphasis added by me.
As I know it's not allowed when sending is done from different
threads. In my case all sending are done from the same thread.
Your problem has nothing to do with threads.
Yes, you can do that as long as the underlying memory (buffer) is not modified until the write handler is called. Calling async_write means you hand over the buffer ownership to Asio. When the write handler is called, the buffer ownership is given back to you.
If am am calling boost::asio::async_write/async_read directly after each other, will the data be ordered? Or do I need to wait on the callback before I am calling write/read again?
Thanks in advance!
The data is not guaranteed to be ordered and if you are using those functions you should wait for the callback before writing again.
(Discussion in terms of async_write, also applies to async_read)
Because async_write is implemented in terms of multiple calls to the underlying stream's async_write_some function, those calls are not atomic. Each call attempts to write data to the stream and has an internal callback to deal with partial operations, in effect waiting on completion as you might code yourself. So you could easily end up with mixed data if you don't wait for completion.
You also need to consider threads. If you call async_x on a stream multiple times you could end up with concurrent operations on the same underlying stream in different threads, leading to undefined behaviour.