diff --git a/doc/index.md b/doc/index.md index 3e864b39..4af167e5 100644 --- a/doc/index.md +++ b/doc/index.md @@ -8,7 +8,7 @@ Feature overview, installation and performance benchmark can be found on [github * Main workhorses of this library: `agrpc::GrpcContext`, `agrpc::GrpcExecutor`. * Asynchronous gRPC clients: [cheat sheet](md_doc_2client__rpc__cheat__sheet.html), `agrpc::ClientRPC`, * Asynchronous gRPC servers: [cheat sheet](md_doc_2server__rpc__cheat__sheet.html), `agrpc::ServerRPC`, `agrpc::register_awaitable_rpc_handler`, -`agrpc::register_yield_rpc_handler`, `agrpc::register_sender_rpc_handler`, `agrpc::register_callback_rpc_handler` +`agrpc::register_yield_rpc_handler`, `agrpc::register_sender_rpc_handler`, `agrpc::register_callback_rpc_handler`, `agrpc::register_coroutine_rpc_handler` * GRPC Timer: `agrpc::Alarm` * Combining GrpcContext and `asio::io_context`: `agrpc::run`, `agrpc::run_completion_queue` * Faster, drop-in replacement for gRPC's [DefaultHealthCheckService](https://github.com/grpc/grpc/blob/v1.50.1/src/cpp/server/health/default_health_check_service.h): `agrpc::HealthCheckService` diff --git a/example/helper/coro_traits.hpp b/example/helper/coro_traits.hpp index e96ec974..831f76c5 100644 --- a/example/helper/coro_traits.hpp +++ b/example/helper/coro_traits.hpp @@ -21,11 +21,11 @@ namespace example { -template +/* [asio-coro-traits] */ +template > struct AsioCoroTraits { - template - using Rebind = boost::asio::experimental::coro; + using ReturnType = boost::asio::experimental::coro; template static boost::asio::deferred_t completion_token(RPCHandler&, CompletionHandler&) @@ -42,6 +42,7 @@ struct AsioCoroTraits boost::asio::detached); } }; +/* [asio-coro-traits] */ } // namespace example #endif // AGRPC_HELPER_CORO_TRAITS_HPP diff --git a/src/agrpc/detail/coroutine_traits.hpp b/src/agrpc/detail/coroutine_traits.hpp index cc587fcd..c903d689 100644 --- a/src/agrpc/detail/coroutine_traits.hpp +++ b/src/agrpc/detail/coroutine_traits.hpp @@ -35,8 +35,7 @@ struct CoroutineTraits; template struct CoroutineTraits> { - template - using Rebind = asio::awaitable; + using ReturnType = asio::awaitable; template static asio::use_awaitable_t completion_token(RPCHandler&, CompletionHandler&) diff --git a/src/agrpc/detail/register_coroutine_rpc_handler.hpp b/src/agrpc/detail/register_coroutine_rpc_handler.hpp index 02dbdb93..a768e18d 100644 --- a/src/agrpc/detail/register_coroutine_rpc_handler.hpp +++ b/src/agrpc/detail/register_coroutine_rpc_handler.hpp @@ -36,13 +36,12 @@ struct RegisterCoroutineRPCHandlerOperation struct Type : detail::RegisterRPCHandlerOperationAsioBase { using Base = detail::RegisterRPCHandlerOperationAsioBase; + using Awaitable = typename CoroTraits::ReturnType; using typename Base::Allocator; using typename Base::RefCountGuard; using typename Base::ServerRPCExecutor; using typename Base::Service; - using Awaitable = typename CoroTraits::template Rebind; - template Type(const ServerRPCExecutor& executor, Service& service, RPCHandler&& rpc_handler, Ch&& completion_handler) : Base(executor, service, static_cast(rpc_handler), static_cast(completion_handler), diff --git a/src/agrpc/register_awaitable_rpc_handler.hpp b/src/agrpc/register_awaitable_rpc_handler.hpp index 14dae62c..c3ba220c 100644 --- a/src/agrpc/register_awaitable_rpc_handler.hpp +++ b/src/agrpc/register_awaitable_rpc_handler.hpp @@ -34,7 +34,7 @@ AGRPC_NAMESPACE_BEGIN() * first argument and `ServerRPC::Request&` as second argument (only for unary and server-streaming rpcs). The ServerRPC * is automatically cancelled at the end of the rpc handler if `finish()` was not called earlier. The return value of * the rpc handler is `co_spawn`ed in a manner similar to: - * `asio::co_spawn(asio::get_associated_executor(completion_handler, executor), rpc_handler())`, where + * `asio::co_spawn(asio::get_associated_executor(completion_handler, executor), rpc_handler)`, where * `completion_handler` is created from `token` and `executor` the first argument passed to this function. * * This asynchronous operation runs forever unless it is cancelled, the rpc handler throws an exception or the server is diff --git a/src/agrpc/register_coroutine_rpc_handler.hpp b/src/agrpc/register_coroutine_rpc_handler.hpp index 7812edc2..87cc3592 100644 --- a/src/agrpc/register_coroutine_rpc_handler.hpp +++ b/src/agrpc/register_coroutine_rpc_handler.hpp @@ -29,7 +29,29 @@ AGRPC_NAMESPACE_BEGIN() /** * @brief (experimental) Register a coroutine rpc handler for the given method * + * The rpc handler will be invoked for every incoming request of this gRPC method. It must take `ServerRPC&` as + * first argument and `ServerRPC::Request&` as second argument (only for unary and server-streaming rpcs). The ServerRPC + * is automatically cancelled at the end of the rpc handler if `finish()` was not called earlier. The return value of + * the rpc handler is `co_spawn`ed in a manner similar to: + * `CoroutineTraits::co_spawn(executor, rpc_handler, completion_handler, function)`, where + * `completion_handler` is created from `token`, `executor` the first argument passed to this function and `function`, + * when invoked, starts waiting for the next rpc. Any arguments passed to `function` will be prepended to the call of + * the rpc handler. The return type of `function` is `CoroutineTraits::ReturnType`, which must be a coroutine, and + * `CoroutineTraits::completion_token` must produce an Asio compatible [completion + * token](https://www.boost.org/doc/libs/1_86_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.completion_tokens_and_handlers) + * that, when used to initiate an asynchronous operation, returns an awaitable. + * + * This asynchronous operation runs forever unless it is cancelled, the rpc handler throws an exception or the server is + * shutdown + * ([grpc::Server::Shutdown](https://grpc.github.io/grpc/cpp/classgrpc_1_1_server_interface.html#a6a1d337270116c95f387e0abf01f6c6c) + * is called). At which point it invokes the completion handler (passing forward the exception thrown by the request + * handler, if any) after all coroutines produced by invoking the rpc handler complete. + * * @tparam ServerRPC An instantiation of `agrpc::ServerRPC` + * @tparam CoroutineTraits A class that provides functions for spawning the coroutine of each rpc. Example: + * + * @snippet coro_traits.hpp asio-coro-traits + * * @param executor The executor used to handle each rpc * @param service The service associated with the gRPC method of the ServerRPC * @param rpc_handler A callable that produces a coroutine. The coroutine's return value is ignored. diff --git a/test/src/test_server_rpc_20.cpp b/test/src/test_server_rpc_20.cpp index cf601296..8ff03b46 100644 --- a/test/src/test_server_rpc_20.cpp +++ b/test/src/test_server_rpc_20.cpp @@ -558,11 +558,10 @@ TEST_CASE_FIXTURE(ServerRPCAwaitableTest, #endif #ifdef AGRPC_TEST_ASIO_HAS_CORO -template +template struct CoroTraits { - template - using Rebind = asio::experimental::coro; + using ReturnType = asio::experimental::coro; template static asio::deferred_t completion_token(RPCHandler&, CompletionHandler&) @@ -608,7 +607,7 @@ TEST_CASE_FIXTURE(ServerRPCAwaitableTest, } }; asio::io_context io_context{1}; - agrpc::register_coroutine_rpc_handler>( + agrpc::register_coroutine_rpc_handler>( get_executor(), service, Handler{}, asio::bind_executor(io_context.get_executor(), test::RethrowFirstArg{})); std::thread t{[&] { @@ -633,8 +632,7 @@ TEST_CASE_FIXTURE(ServerRPCAwaitableTest, #ifdef AGRPC_TEST_HAS_BOOST_COBALT struct BoostCobaltTraits { - template - using Rebind = boost::cobalt::task; + using ReturnType = boost::cobalt::task; template static boost::cobalt::use_op_t completion_token(RPCHandler&, CompletionHandler&) diff --git a/test/src/test_unifex_20.cpp b/test/src/test_unifex_20.cpp index 6ef2df10..4ff0ba79 100644 --- a/test/src/test_unifex_20.cpp +++ b/test/src/test_unifex_20.cpp @@ -247,8 +247,7 @@ TEST_CASE_FIXTURE(test::ExecutionGrpcContextTest, "unifex Waiter: initiate alarm #if defined(AGRPC_TEST_ASIO_HAS_CORO) && !UNIFEX_NO_COROUTINES struct UnifexCoroutineTraits { - template - using Rebind = unifex::task; + using ReturnType = unifex::task; template static auto completion_token(RPCHandler&, CompletionHandler&)