asio-grpc v1.6.0
Asynchronous gRPC with Asio/unified executors
Difference to Asio

The free functions in this library (except agrpc::repeatedly_request) require that the completion handler executor be created from a agrpc::GrpcExecutor and they use that as the I/O executor. Unlike Asio, where the I/O executor is obtained from the first argument to the initiating function. Example:

asio::steady_timer timer{exec1}; // exec1 is the I/O executor
timer.async_wait(asio::bind_executor(exec2, [](auto&&) {})); // exec2 is the completion handler executor

In asio-grpc:

grpc::Alarm alarm;
agrpc::wait(alarm, deadline, asio::bind_executor(grpc_context.get_executor(), [](auto&&) {}));
// grpc_context.get_executor() is both, the I/O executor and completion handler executor
constexpr detail::WaitFn wait
Wait for a timer.
Definition: wait.hpp:78

As a consequence, asynchronous operations in asio-grpc always complete in the thread that called GrpcContext::run()/poll(), whereas Asio would submit the completion handler for execution as if by performing asio::dispatch(exec2, [=<moved>]() { completion_handler(args...); }). See also Asio's documentation.
Please open a ticket if you would like to have a wrapper class in asio-grpc that mimics that behavior, it is rather easy to implement.


Until asio-grpc v1.8.0 the completion handler's allocator was retrieved in a manner equivalent to:

asio::get_associated_allocator(completion_handler, asio::query(asio::get_associated_executor(completion_handler), asio::execution::allocator));

Since v1.8.0 the allocator is retrieved using:

asio::get_associated_allocator(completion_handler);

And for agrpc::repeatedly_request using:

asio::get_associated_allocator(completion_handler, asio::get_associated_allocator(request_handler));