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 calling f()
.
The asio_handler_invoke()
function is located using argument-dependent lookup. The function boost::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.