Reputation: 2990
The code:
In thread 1:
boost::async_read(socket, buffer, strand.wrap(read_handler));
In thread 2:
strand.post([](){socket.async_write_some(buffer, strand.wrap(write_handler))});
It is clear that read_handler, async_write_some, write_handler protected by strand, they will not concurrent. However, async_read is an composed operation, it will call zero or more times to async_read_some, those async_read_some also need protect by strand or else they might concurrent with async_write_some in thread 2.
But from the code, strand only wrap read_handler, how asio make all intermediate operations(async_read_some) also wrapped by the strand?
Upvotes: 2
Views: 1145
Reputation: 51941
In short, asio_handler_invoke
enables one to customize the invocation of handlers in the context of a different handler. In this case, the object returned from strand.wrap()
has a custom asio_handler_invoke
strategy associated with it that will dispatch handlers into the strand that wrapped the initial handler. Conceptually, it is as follows:
template <typename Handler>
struct strand_handler
{
void operator()();
Handler handler_;
boost::asio::strand dispatcher_;
};
// Customize invocation of Function within context of custom_handler.
template <typename Function>
void asio_handler_invoke(Function function, strand_handler* context)
{
context->dispatcher_.dispatch(function);
}
strand_handler wrapped_completion_handler = strand.wrap(completion_handler);
using boost::asio::asio_handler_invoke;
asio_handler_invoke(intermediate_handler, &wrapped_completion_handler);
The custom asio_handler_invoke
hook is located via argument-dependent lookup. This detail is documented in the Handler requirement:
Causes the function object
f
to be executed as if by callingf()
.The
asio_handler_invoke()
function is located using argument-dependent lookup. The functionboost::asio::asio_handler_invoke()
serves as a default if no user-supplied function is available.
For more details on asio_handler_invoke
, consider reading this answer.
Be aware that an operation may be attempted within the initiating function. The documentation is specific that intermediate handlers will be invoked within the same context as the final completion handler. Therefore, given:
assert(strand.running_in_this_thread());
boost::async_read(socket, buffer, strand.wrap(read_handler));
the boost::async_read
itself must be invoked within the context of strand
to be thread-safe. See this answer for more details on thread-safety and strands.
Upvotes: 3