From be6979202bb15b72c7b9838152081034d2e370d6 Mon Sep 17 00:00:00 2001 From: Vijay Pai Date: Wed, 20 Nov 2019 16:56:18 -0800 Subject: [PATCH] Revamp server-side C++ callback API, message allocator, and mocking --- BUILD | 3 + BUILD.gn | 2 + CMakeLists.txt | 6 + Makefile | 6 + build.yaml | 3 + gRPC-C++.podspec | 2 + grpc.gyp | 2 + include/grpc/impl/codegen/grpc_types.h | 4 + .../impl/codegen/async_generic_service.h | 68 +- include/grpcpp/impl/codegen/callback_common.h | 18 +- .../impl/codegen/client_callback_impl.h | 28 +- .../grpcpp/impl/codegen/client_context_impl.h | 13 + .../impl/codegen/completion_queue_impl.h | 6 +- .../grpcpp/impl/codegen/method_handler_impl.h | 37 +- .../grpcpp/impl/codegen/rpc_service_method.h | 11 +- include/grpcpp/impl/codegen/server_callback.h | 12 +- .../impl/codegen/server_callback_handlers.h | 814 +++++++++++ .../impl/codegen/server_callback_impl.h | 1285 ++++++----------- include/grpcpp/impl/codegen/server_context.h | 8 + .../grpcpp/impl/codegen/server_context_impl.h | 271 +++- .../grpcpp/impl/codegen/server_interceptor.h | 14 +- .../grpcpp/test/default_reactor_test_peer.h | 55 + src/compiler/cpp_generator.cc | 94 +- src/core/lib/surface/completion_queue.cc | 3 +- src/cpp/client/channel_cc.cc | 9 +- src/cpp/client/client_context.cc | 17 +- src/cpp/server/server_callback.cc | 52 + src/cpp/server/server_cc.cc | 42 +- src/cpp/server/server_context.cc | 116 +- test/core/end2end/inproc_callback_test.cc | 2 + test/core/end2end/tests/connectivity.cc | 1 + test/core/iomgr/threadpool_test.cc | 2 + test/core/surface/completion_queue_test.cc | 3 + test/cpp/codegen/compiler_test_golden | 75 +- test/cpp/end2end/end2end_test.cc | 100 +- test/cpp/end2end/hybrid_end2end_test.cc | 14 +- .../end2end/message_allocator_end2end_test.cc | 12 +- test/cpp/end2end/mock_test.cc | 85 +- test/cpp/end2end/test_service_impl.cc | 599 ++++---- test/cpp/end2end/test_service_impl.h | 44 +- test/cpp/microbenchmarks/bm_cq.cc | 2 + test/cpp/microbenchmarks/bm_threadpool.cc | 4 + .../microbenchmarks/callback_test_service.cc | 21 +- .../microbenchmarks/callback_test_service.h | 11 +- test/cpp/qps/server_callback.cc | 19 +- tools/doxygen/Doxyfile.c++ | 1 + tools/doxygen/Doxyfile.c++.internal | 2 + 47 files changed, 2390 insertions(+), 1608 deletions(-) create mode 100644 include/grpcpp/impl/codegen/server_callback_handlers.h create mode 100644 include/grpcpp/test/default_reactor_test_peer.h create mode 100644 src/cpp/server/server_callback.cc diff --git a/BUILD b/BUILD index 7cc070e4ba0..54d653f98cb 100644 --- a/BUILD +++ b/BUILD @@ -150,6 +150,7 @@ GRPCXX_SRCS = [ "src/cpp/server/health/health_check_service.cc", "src/cpp/server/health/health_check_service_server_builder_option.cc", "src/cpp/server/server_builder.cc", + "src/cpp/server/server_callback.cc", "src/cpp/server/server_cc.cc", "src/cpp/server/server_context.cc", "src/cpp/server/server_credentials.cc", @@ -2129,6 +2130,7 @@ grpc_cc_library( "include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_callback_handlers.h", "include/grpcpp/impl/codegen/server_callback_impl.h", "include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context_impl.h", @@ -2238,6 +2240,7 @@ grpc_cc_library( "include/grpc++/test/server_context_test_spouse.h", "include/grpcpp/test/mock_stream.h", "include/grpcpp/test/server_context_test_spouse.h", + "include/grpcpp/test/default_reactor_test_peer.h", ], deps = [ ":grpc++", diff --git a/BUILD.gn b/BUILD.gn index a69a8e0940a..51b77056646 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1120,6 +1120,7 @@ config("grpc_config") { "include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_callback_handlers.h", "include/grpcpp/impl/codegen/server_callback_impl.h", "include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context_impl.h", @@ -1653,6 +1654,7 @@ config("grpc_config") { "src/cpp/server/secure_server_credentials.cc", "src/cpp/server/secure_server_credentials.h", "src/cpp/server/server_builder.cc", + "src/cpp/server/server_callback.cc", "src/cpp/server/server_cc.cc", "src/cpp/server/server_context.cc", "src/cpp/server/server_credentials.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index e2b550c46d8..40e8f3c8742 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3288,6 +3288,7 @@ add_library(grpc++ src/cpp/server/health/health_check_service.cc src/cpp/server/health/health_check_service_server_builder_option.cc src/cpp/server/server_builder.cc + src/cpp/server/server_callback.cc src/cpp/server/server_cc.cc src/cpp/server/server_context.cc src/cpp/server/server_credentials.cc @@ -3780,6 +3781,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_handlers.h include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h @@ -4270,6 +4272,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_handlers.h include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h @@ -4466,6 +4469,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_handlers.h include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h @@ -4550,6 +4554,7 @@ add_library(grpc++_unsecure src/cpp/server/health/health_check_service.cc src/cpp/server/health/health_check_service_server_builder_option.cc src/cpp/server/server_builder.cc + src/cpp/server/server_callback.cc src/cpp/server/server_cc.cc src/cpp/server/server_context.cc src/cpp/server/server_credentials.cc @@ -5041,6 +5046,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_handlers.h include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h diff --git a/Makefile b/Makefile index 5088c9d8db0..0c4e5920ff2 100644 --- a/Makefile +++ b/Makefile @@ -5685,6 +5685,7 @@ LIBGRPC++_SRC = \ src/cpp/server/health/health_check_service.cc \ src/cpp/server/health/health_check_service_server_builder_option.cc \ src/cpp/server/server_builder.cc \ + src/cpp/server/server_callback.cc \ src/cpp/server/server_cc.cc \ src/cpp/server/server_context.cc \ src/cpp/server/server_credentials.cc \ @@ -6138,6 +6139,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_handlers.h \ include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ @@ -6610,6 +6612,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_handlers.h \ include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ @@ -6789,6 +6792,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_handlers.h \ include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ @@ -6913,6 +6917,7 @@ LIBGRPC++_UNSECURE_SRC = \ src/cpp/server/health/health_check_service.cc \ src/cpp/server/health/health_check_service_server_builder_option.cc \ src/cpp/server/server_builder.cc \ + src/cpp/server/server_callback.cc \ src/cpp/server/server_cc.cc \ src/cpp/server/server_context.cc \ src/cpp/server/server_credentials.cc \ @@ -7366,6 +7371,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_handlers.h \ include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ diff --git a/build.yaml b/build.yaml index 02430b1c87e..7b35307df7e 100644 --- a/build.yaml +++ b/build.yaml @@ -397,6 +397,7 @@ filegroups: - include/grpcpp/impl/codegen/security/auth_context.h - include/grpcpp/impl/codegen/serialization_traits.h - include/grpcpp/impl/codegen/server_callback.h + - include/grpcpp/impl/codegen/server_callback_handlers.h - include/grpcpp/impl/codegen/server_callback_impl.h - include/grpcpp/impl/codegen/server_context.h - include/grpcpp/impl/codegen/server_context_impl.h @@ -587,6 +588,7 @@ filegroups: - src/cpp/server/health/health_check_service.cc - src/cpp/server/health/health_check_service_server_builder_option.cc - src/cpp/server/server_builder.cc + - src/cpp/server/server_callback.cc - src/cpp/server/server_cc.cc - src/cpp/server/server_context.cc - src/cpp/server/server_credentials.cc @@ -618,6 +620,7 @@ filegroups: public_headers: - include/grpc++/test/mock_stream.h - include/grpc++/test/server_context_test_spouse.h + - include/grpcpp/test/default_reactor_test_peer.h - include/grpcpp/test/mock_stream.h - include/grpcpp/test/server_context_test_spouse.h deps: diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index d9cdbb155d2..fe40d3e6107 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -136,6 +136,7 @@ Pod::Spec.new do |s| 'include/grpcpp/impl/codegen/security/auth_context.h', 'include/grpcpp/impl/codegen/serialization_traits.h', 'include/grpcpp/impl/codegen/server_callback.h', + 'include/grpcpp/impl/codegen/server_callback_handlers.h', 'include/grpcpp/impl/codegen/server_callback_impl.h', 'include/grpcpp/impl/codegen/server_context.h', 'include/grpcpp/impl/codegen/server_context_impl.h', @@ -260,6 +261,7 @@ Pod::Spec.new do |s| 'src/cpp/server/secure_server_credentials.cc', 'src/cpp/server/secure_server_credentials.h', 'src/cpp/server/server_builder.cc', + 'src/cpp/server/server_callback.cc', 'src/cpp/server/server_cc.cc', 'src/cpp/server/server_context.cc', 'src/cpp/server/server_credentials.cc', diff --git a/grpc.gyp b/grpc.gyp index 3d62307f057..5b6dbd3d44b 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -1541,6 +1541,7 @@ 'src/cpp/server/health/health_check_service.cc', 'src/cpp/server/health/health_check_service_server_builder_option.cc', 'src/cpp/server/server_builder.cc', + 'src/cpp/server/server_callback.cc', 'src/cpp/server/server_cc.cc', 'src/cpp/server/server_context.cc', 'src/cpp/server/server_credentials.cc', @@ -1895,6 +1896,7 @@ 'src/cpp/server/health/health_check_service.cc', 'src/cpp/server/health/health_check_service_server_builder_option.cc', 'src/cpp/server/server_builder.cc', + 'src/cpp/server/server_callback.cc', 'src/cpp/server/server_cc.cc', 'src/cpp/server/server_context.cc', 'src/cpp/server/server_credentials.cc', diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index cb28de0f9b1..836441f8948 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -726,6 +726,10 @@ typedef struct grpc_experimental_completion_queue_functor { operation succeeded (non-zero) or failed (zero) */ void (*functor_run)(struct grpc_experimental_completion_queue_functor*, int); + /** The inlineable member specifies whether this functor can be run inline. + This should only be used for trivial internally-defined functors. */ + int inlineable; + /** The following fields are not API. They are meant for internal use. */ int internal_success; struct grpc_experimental_completion_queue_functor* internal_next; diff --git a/include/grpcpp/impl/codegen/async_generic_service.h b/include/grpcpp/impl/codegen/async_generic_service.h index c7dd5f7d24a..934f619a446 100644 --- a/include/grpcpp/impl/codegen/async_generic_service.h +++ b/include/grpcpp/impl/codegen/async_generic_service.h @@ -21,6 +21,7 @@ #include #include +#include #include struct grpc_server; @@ -42,12 +43,12 @@ class GenericServerContext final : public ::grpc_impl::ServerContext { private: friend class grpc_impl::Server; - friend class ServerInterface; + friend class grpc::ServerInterface; void Clear() { method_.clear(); host_.clear(); - ServerContext::Clear(); + ::grpc_impl::ServerContext::Clear(); } grpc::string method_; @@ -89,39 +90,30 @@ class AsyncGenericService final { namespace experimental { /// \a ServerGenericBidiReactor is the reactor class for bidi streaming RPCs -/// invoked on a CallbackGenericService. The API difference relative to -/// ServerBidiReactor is that the argument to OnStarted is a -/// GenericServerContext rather than a ServerContext. All other reaction and -/// operation initiation APIs are the same as ServerBidiReactor. -class ServerGenericBidiReactor - : public ::grpc_impl::experimental::ServerBidiReactor { +/// invoked on a CallbackGenericService. It is just a ServerBidi reactor with +/// ByteBuffer arguments. +using ServerGenericBidiReactor = + ::grpc_impl::experimental::ServerBidiReactor; + +class GenericCallbackServerContext final + : public ::grpc_impl::experimental::CallbackServerContext { public: - /// Similar to ServerBidiReactor::OnStarted except for argument type. - /// - /// \param[in] context The context object associated with this RPC. - virtual void OnStarted(GenericServerContext* /*context*/) {} + const grpc::string& method() const { return method_; } + const grpc::string& host() const { return host_; } private: - void OnStarted(::grpc_impl::ServerContext* ctx) final { - OnStarted(static_cast(ctx)); - } -}; - -} // namespace experimental + friend class ::grpc_impl::Server; + friend class ::grpc::ServerInterface; -namespace internal { -class UnimplementedGenericBidiReactor - : public experimental::ServerGenericBidiReactor { - public: - void OnDone() override { delete this; } - void OnStarted(GenericServerContext*) override { - this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); + void Clear() { + method_.clear(); + host_.clear(); + ::grpc_impl::experimental::CallbackServerContext::Clear(); } -}; -} // namespace internal -namespace experimental { + grpc::string method_; + grpc::string host_; +}; /// \a CallbackGenericService is the base class for generic services implemented /// using the callback API and registered through the ServerBuilder using @@ -132,10 +124,16 @@ class CallbackGenericService { virtual ~CallbackGenericService() {} /// The "method handler" for the generic API. This function should be - /// overridden to return a ServerGenericBidiReactor that implements the - /// application-level interface for this RPC. - virtual ServerGenericBidiReactor* CreateReactor() { - return new internal::UnimplementedGenericBidiReactor; + /// overridden to provide a ServerGenericBidiReactor that implements the + /// application-level interface for this RPC. Unimplemented by default. + virtual ServerGenericBidiReactor* CreateReactor( + GenericCallbackServerContext* /*ctx*/) { + class Reactor : public ServerGenericBidiReactor { + public: + Reactor() { this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); } + void OnDone() override { delete this; } + }; + return new Reactor; } private: @@ -145,7 +143,9 @@ class CallbackGenericService { Handler() { return new ::grpc_impl::internal::CallbackBidiHandler( - [this] { return CreateReactor(); }); + [this](::grpc_impl::experimental::CallbackServerContext* ctx) { + return CreateReactor(static_cast(ctx)); + }); } grpc_impl::Server* server_{nullptr}; diff --git a/include/grpcpp/impl/codegen/callback_common.h b/include/grpcpp/impl/codegen/callback_common.h index 9b6fe4527a5..6b4cbdec03f 100644 --- a/include/grpcpp/impl/codegen/callback_common.h +++ b/include/grpcpp/impl/codegen/callback_common.h @@ -47,8 +47,8 @@ void CatchingCallback(Func&& func, Args&&... args) { #endif // GRPC_ALLOW_EXCEPTIONS } -template -ReturnType* CatchingReactorCreator(Func&& func, Args&&... args) { +template +Reactor* CatchingReactorGetter(Func&& func, Args&&... args) { #if GRPC_ALLOW_EXCEPTIONS try { return func(std::forward(args)...); @@ -85,6 +85,10 @@ class CallbackWithStatusTag : call_(call), func_(std::move(f)), ops_(ops) { g_core_codegen_interface->grpc_call_ref(call); functor_run = &CallbackWithStatusTag::StaticRun; + // A client-side callback should never be run inline since they will always + // have work to do from the user application. So, set the parent's + // inlineable field to false + inlineable = false; } ~CallbackWithStatusTag() {} Status* status_ptr() { return &status_; } @@ -147,8 +151,8 @@ class CallbackWithSuccessTag CallbackWithSuccessTag() : call_(nullptr) {} CallbackWithSuccessTag(grpc_call* call, std::function f, - CompletionQueueTag* ops) { - Set(call, f, ops); + CompletionQueueTag* ops, bool can_inline) { + Set(call, f, ops, can_inline); } CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete; @@ -159,14 +163,18 @@ class CallbackWithSuccessTag // Set can only be called on a default-constructed or Clear'ed tag. // It should never be called on a tag that was constructed with arguments // or on a tag that has been Set before unless the tag has been cleared. + // can_inline indicates that this particular callback can be executed inline + // (without needing a thread hop) and is only used for library-provided server + // callbacks. void Set(grpc_call* call, std::function f, - CompletionQueueTag* ops) { + CompletionQueueTag* ops, bool can_inline) { GPR_CODEGEN_ASSERT(call_ == nullptr); g_core_codegen_interface->grpc_call_ref(call); call_ = call; func_ = std::move(f); ops_ = ops; functor_run = &CallbackWithSuccessTag::StaticRun; + inlineable = can_inline; } void Clear() { diff --git a/include/grpcpp/impl/codegen/client_callback_impl.h b/include/grpcpp/impl/codegen/client_callback_impl.h index e9bec012114..7eb88a7b26e 100644 --- a/include/grpcpp/impl/codegen/client_callback_impl.h +++ b/include/grpcpp/impl/codegen/client_callback_impl.h @@ -457,7 +457,7 @@ class ClientCallbackReaderWriterImpl reactor_->OnReadInitialMetadataDone(ok); MaybeFinish(); }, - &start_ops_); + &start_ops_, /*can_inline=*/false); if (!start_corked_) { start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, context_->initial_metadata_flags()); @@ -473,7 +473,7 @@ class ClientCallbackReaderWriterImpl reactor_->OnWriteDone(ok); MaybeFinish(); }, - &write_ops_); + &write_ops_, /*can_inline=*/false); write_ops_.set_core_cq_tag(&write_tag_); read_tag_.Set(call_.call(), @@ -481,7 +481,7 @@ class ClientCallbackReaderWriterImpl reactor_->OnReadDone(ok); MaybeFinish(); }, - &read_ops_); + &read_ops_, /*can_inline=*/false); read_ops_.set_core_cq_tag(&read_tag_); if (read_ops_at_start_) { call_.PerformOps(&read_ops_); @@ -496,7 +496,7 @@ class ClientCallbackReaderWriterImpl } finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, - &finish_ops_); + &finish_ops_, /*can_inline=*/false); finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.set_core_cq_tag(&finish_tag_); call_.PerformOps(&finish_ops_); @@ -544,7 +544,7 @@ class ClientCallbackReaderWriterImpl reactor_->OnWritesDoneDone(ok); MaybeFinish(); }, - &writes_done_ops_); + &writes_done_ops_, /*can_inline=*/false); writes_done_ops_.set_core_cq_tag(&writes_done_tag_); callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); if (started_) { @@ -668,7 +668,7 @@ class ClientCallbackReaderImpl reactor_->OnReadInitialMetadataDone(ok); MaybeFinish(); }, - &start_ops_); + &start_ops_, /*can_inline=*/false); start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, context_->initial_metadata_flags()); start_ops_.RecvInitialMetadata(context_); @@ -681,14 +681,14 @@ class ClientCallbackReaderImpl reactor_->OnReadDone(ok); MaybeFinish(); }, - &read_ops_); + &read_ops_, /*can_inline=*/false); read_ops_.set_core_cq_tag(&read_tag_); if (read_ops_at_start_) { call_.PerformOps(&read_ops_); } finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, - &finish_ops_); + &finish_ops_, /*can_inline=*/false); finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.set_core_cq_tag(&finish_tag_); call_.PerformOps(&finish_ops_); @@ -808,7 +808,7 @@ class ClientCallbackWriterImpl reactor_->OnReadInitialMetadataDone(ok); MaybeFinish(); }, - &start_ops_); + &start_ops_, /*can_inline=*/false); if (!start_corked_) { start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, context_->initial_metadata_flags()); @@ -824,7 +824,7 @@ class ClientCallbackWriterImpl reactor_->OnWriteDone(ok); MaybeFinish(); }, - &write_ops_); + &write_ops_, /*can_inline=*/false); write_ops_.set_core_cq_tag(&write_tag_); if (write_ops_at_start_) { @@ -836,7 +836,7 @@ class ClientCallbackWriterImpl } finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, - &finish_ops_); + &finish_ops_, /*can_inline=*/false); finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.set_core_cq_tag(&finish_tag_); call_.PerformOps(&finish_ops_); @@ -874,7 +874,7 @@ class ClientCallbackWriterImpl reactor_->OnWritesDoneDone(ok); MaybeFinish(); }, - &writes_done_ops_); + &writes_done_ops_, /*can_inline=*/false); writes_done_ops_.set_core_cq_tag(&writes_done_tag_); callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); if (started_) { @@ -983,7 +983,7 @@ class ClientCallbackUnaryImpl final : public experimental::ClientCallbackUnary { reactor_->OnReadInitialMetadataDone(ok); MaybeFinish(); }, - &start_ops_); + &start_ops_, /*can_inline=*/false); start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, context_->initial_metadata_flags()); start_ops_.RecvInitialMetadata(context_); @@ -991,7 +991,7 @@ class ClientCallbackUnaryImpl final : public experimental::ClientCallbackUnary { call_.PerformOps(&start_ops_); finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, - &finish_ops_); + &finish_ops_, /*can_inline=*/false); finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.set_core_cq_tag(&finish_tag_); call_.PerformOps(&finish_ops_); diff --git a/include/grpcpp/impl/codegen/client_context_impl.h b/include/grpcpp/impl/codegen/client_context_impl.h index 1d4806d41ae..48a02dbd4cb 100644 --- a/include/grpcpp/impl/codegen/client_context_impl.h +++ b/include/grpcpp/impl/codegen/client_context_impl.h @@ -66,6 +66,7 @@ template class BlockingUnaryCallImpl; class CallOpClientRecvStatus; class CallOpRecvInitialMetadata; +class ServerContextImpl; } // namespace internal namespace testing { @@ -106,6 +107,11 @@ class ClientAsyncReaderWriter; template class ClientAsyncResponseReader; +namespace experimental { +class ServerContextBase; +class CallbackServerContext; +} // namespace experimental + /// Options for \a ClientContext::FromServerContext specifying which traits from /// the \a ServerContext to propagate (copy) from it into a new \a /// ClientContext. @@ -195,6 +201,9 @@ class ClientContext { static std::unique_ptr FromServerContext( const grpc_impl::ServerContext& server_context, PropagationOptions options = PropagationOptions()); + static std::unique_ptr FromCallbackServerContext( + const grpc_impl::experimental::CallbackServerContext& server_context, + PropagationOptions options = PropagationOptions()); /// Add the (\a meta_key, \a meta_value) pair to the metadata associated with /// a client call. These are made available at the server side by the \a @@ -474,6 +483,10 @@ class ClientContext { void SendCancelToInterceptors(); + static std::unique_ptr FromInternalServerContext( + const grpc_impl::experimental::ServerContextBase& server_context, + PropagationOptions options); + bool initial_metadata_received_; bool wait_for_ready_; bool wait_for_ready_explicitly_set_; diff --git a/include/grpcpp/impl/codegen/completion_queue_impl.h b/include/grpcpp/impl/codegen/completion_queue_impl.h index 7cb3d2288c6..04b9300e032 100644 --- a/include/grpcpp/impl/codegen/completion_queue_impl.h +++ b/include/grpcpp/impl/codegen/completion_queue_impl.h @@ -46,7 +46,6 @@ namespace grpc_impl { class Channel; class Server; class ServerBuilder; -class ServerContext; template class ClientReader; template @@ -57,6 +56,9 @@ template class ServerReader; template class ServerWriter; +namespace experimental { +class ServerContextBase; +} // namespace experimental namespace internal { template class ServerReaderWriterBody; @@ -275,7 +277,7 @@ class CompletionQueue : private ::grpc::GrpcLibraryCodegen { template <::grpc::StatusCode code> friend class ::grpc_impl::internal::ErrorMethodHandler; friend class ::grpc_impl::Server; - friend class ::grpc_impl::ServerContext; + friend class ::grpc_impl::experimental::ServerContextBase; friend class ::grpc::ServerInterface; template friend class ::grpc::internal::BlockingUnaryCallImpl; diff --git a/include/grpcpp/impl/codegen/method_handler_impl.h b/include/grpcpp/impl/codegen/method_handler_impl.h index f65a1736ad2..c7545fdcc13 100644 --- a/include/grpcpp/impl/codegen/method_handler_impl.h +++ b/include/grpcpp/impl/codegen/method_handler_impl.h @@ -65,8 +65,10 @@ class RpcMethodHandler : public ::grpc::internal::MethodHandler { ::grpc::Status status = param.status; if (status.ok()) { status = CatchingFunctionHandler([this, ¶m, &rsp] { - return func_(service_, param.server_context, - static_cast(param.request), &rsp); + return func_( + service_, + static_cast<::grpc_impl::ServerContext*>(param.server_context), + static_cast(param.request), &rsp); }); static_cast(param.request)->~RequestType(); } @@ -128,12 +130,16 @@ class ClientStreamingHandler : public ::grpc::internal::MethodHandler { : func_(func), service_(service) {} void RunHandler(const HandlerParameter& param) final { - ::grpc_impl::ServerReader reader(param.call, - param.server_context); + ::grpc_impl::ServerReader reader( + param.call, + static_cast<::grpc_impl::ServerContext*>(param.server_context)); ResponseType rsp; ::grpc::Status status = CatchingFunctionHandler([this, ¶m, &reader, &rsp] { - return func_(service_, param.server_context, &reader, &rsp); + return func_( + service_, + static_cast<::grpc_impl::ServerContext*>(param.server_context), + &reader, &rsp); }); ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, @@ -178,11 +184,14 @@ class ServerStreamingHandler : public ::grpc::internal::MethodHandler { void RunHandler(const HandlerParameter& param) final { ::grpc::Status status = param.status; if (status.ok()) { - ::grpc_impl::ServerWriter writer(param.call, - param.server_context); + ::grpc_impl::ServerWriter writer( + param.call, + static_cast<::grpc_impl::ServerContext*>(param.server_context)); status = CatchingFunctionHandler([this, ¶m, &writer] { - return func_(service_, param.server_context, - static_cast(param.request), &writer); + return func_( + service_, + static_cast<::grpc_impl::ServerContext*>(param.server_context), + static_cast(param.request), &writer); }); static_cast(param.request)->~RequestType(); } @@ -246,9 +255,12 @@ class TemplatedBidiStreamingHandler : public ::grpc::internal::MethodHandler { : func_(func), write_needed_(WriteNeeded) {} void RunHandler(const HandlerParameter& param) final { - Streamer stream(param.call, param.server_context); + Streamer stream(param.call, static_cast<::grpc_impl::ServerContext*>( + param.server_context)); ::grpc::Status status = CatchingFunctionHandler([this, ¶m, &stream] { - return func_(param.server_context, &stream); + return func_( + static_cast<::grpc_impl::ServerContext*>(param.server_context), + &stream); }); ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, @@ -333,7 +345,8 @@ template <::grpc::StatusCode code> class ErrorMethodHandler : public ::grpc::internal::MethodHandler { public: template - static void FillOps(::grpc_impl::ServerContext* context, T* ops) { + static void FillOps(::grpc_impl::experimental::ServerContextBase* context, + T* ops) { ::grpc::Status status(code, ""); if (!context->sent_initial_metadata_) { ops->SendInitialMetadata(&context->initial_metadata_, diff --git a/include/grpcpp/impl/codegen/rpc_service_method.h b/include/grpcpp/impl/codegen/rpc_service_method.h index ad48e2071ca..7c5b8bf0c33 100644 --- a/include/grpcpp/impl/codegen/rpc_service_method.h +++ b/include/grpcpp/impl/codegen/rpc_service_method.h @@ -32,8 +32,10 @@ #include namespace grpc_impl { -class ServerContext; +namespace experimental { +class ServerContextBase; } +} // namespace grpc_impl namespace grpc { namespace internal { @@ -52,8 +54,9 @@ class MethodHandler { /// \param requester : used only by the callback API. It is a function /// called by the RPC Controller to request another RPC (and also /// to set up the state required to make that request possible) - HandlerParameter(Call* c, ::grpc_impl::ServerContext* context, void* req, - Status req_status, void* handler_data, + HandlerParameter(Call* c, + ::grpc_impl::experimental::ServerContextBase* context, + void* req, Status req_status, void* handler_data, std::function requester) : call(c), server_context(context), @@ -63,7 +66,7 @@ class MethodHandler { call_requester(std::move(requester)) {} ~HandlerParameter() {} Call* const call; - ::grpc_impl::ServerContext* const server_context; + ::grpc_impl::experimental::ServerContextBase* const server_context; void* const request; const Status status; void* const internal_data; diff --git a/include/grpcpp/impl/codegen/server_callback.h b/include/grpcpp/impl/codegen/server_callback.h index cb771a662b9..d33a224da2c 100644 --- a/include/grpcpp/impl/codegen/server_callback.h +++ b/include/grpcpp/impl/codegen/server_callback.h @@ -23,20 +23,18 @@ namespace grpc { namespace experimental { -template -using ServerReadReactor = - ::grpc_impl::experimental::ServerReadReactor; +template +using ServerReadReactor = ::grpc_impl::experimental::ServerReadReactor; -template +template using ServerWriteReactor = - ::grpc_impl::experimental::ServerWriteReactor; + ::grpc_impl::experimental::ServerWriteReactor; template using ServerBidiReactor = ::grpc_impl::experimental::ServerBidiReactor; -typedef ::grpc_impl::experimental::ServerCallbackRpcController - ServerCallbackRpcController; +using ServerUnaryReactor = ::grpc_impl::experimental::ServerUnaryReactor; } // namespace experimental } // namespace grpc diff --git a/include/grpcpp/impl/codegen/server_callback_handlers.h b/include/grpcpp/impl/codegen/server_callback_handlers.h new file mode 100644 index 00000000000..32d19d765fa --- /dev/null +++ b/include/grpcpp/impl/codegen/server_callback_handlers.h @@ -0,0 +1,814 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_HANDLERS_H +#define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_HANDLERS_H + +#include +#include +#include +#include +#include + +namespace grpc_impl { +namespace internal { + +template +class CallbackUnaryHandler : public ::grpc::internal::MethodHandler { + public: + explicit CallbackUnaryHandler( + std::function + get_reactor) + : get_reactor_(std::move(get_reactor)) {} + + void SetMessageAllocator( + ::grpc::experimental::MessageAllocator* + allocator) { + allocator_ = allocator; + } + + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a controller structure (that includes request/response) + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + auto* allocator_state = static_cast< + ::grpc::experimental::MessageHolder*>( + param.internal_data); + + auto* call = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackUnaryImpl))) + ServerCallbackUnaryImpl( + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + param.call, allocator_state, std::move(param.call_requester)); + param.server_context->BeginCompletionOp( + param.call, [call](bool) { call->MaybeDone(); }, call); + + experimental::ServerUnaryReactor* reactor = nullptr; + if (param.status.ok()) { + reactor = ::grpc::internal::CatchingReactorGetter< + experimental::ServerUnaryReactor>( + get_reactor_, + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + call->request(), call->response()); + } + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(UnimplementedUnaryReactor))) + UnimplementedUnaryReactor( + ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } + + /// Invoke SetupReactor as the last part of the handler + call->SetupReactor(reactor); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + ::grpc::Status* status, void** handler_data) final { + ::grpc::ByteBuffer buf; + buf.set_buffer(req); + RequestType* request = nullptr; + ::grpc::experimental::MessageHolder* + allocator_state = nullptr; + if (allocator_ != nullptr) { + allocator_state = allocator_->AllocateMessages(); + } else { + allocator_state = + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(DefaultMessageHolder))) + DefaultMessageHolder(); + } + *handler_data = allocator_state; + request = allocator_state->request(); + *status = + ::grpc::SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + // Clean up on deserialization failure. + allocator_state->Release(); + return nullptr; + } + + private: + std::function + get_reactor_; + ::grpc::experimental::MessageAllocator* + allocator_ = nullptr; + + class ServerCallbackUnaryImpl : public experimental::ServerCallbackUnary { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set( + call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_, + reactor_.load(std::memory_order_relaxed)->InternalInlineable()); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (s.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessagePtr(response())); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + } + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + this->Ref(); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed) + ->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_, false); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + private: + friend class CallbackUnaryHandler; + + ServerCallbackUnaryImpl( + ::grpc_impl::experimental::CallbackServerContext* ctx, + ::grpc::internal::Call* call, + ::grpc::experimental::MessageHolder* + allocator_state, + std::function call_requester) + : ctx_(ctx), + call_(*call), + allocator_state_(allocator_state), + call_requester_(std::move(call_requester)) { + ctx_->set_message_allocator_state(allocator_state); + } + + /// SetupReactor binds the reactor (which also releases any queued + /// operations), maybe calls OnCancel if possible/needed, and maybe marks + /// the completion of the RPC. This should be the last component of the + /// handler. + void SetupReactor(experimental::ServerUnaryReactor* reactor) { + reactor_.store(reactor, std::memory_order_relaxed); + this->BindReactor(reactor); + this->MaybeCallOnCancel(reactor); + this->MaybeDone(); + } + + const RequestType* request() { return allocator_state_->request(); } + ResponseType* response() { return allocator_state_->response(); } + + void MaybeDone() override { + if (GPR_UNLIKELY(this->Unref() == 1)) { + reactor_.load(std::memory_order_relaxed)->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + allocator_state_->Release(); + this->~ServerCallbackUnaryImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + ServerReactor* reactor() override { + return reactor_.load(std::memory_order_relaxed); + } + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallbackWithSuccessTag meta_tag_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; + ::grpc::internal::CallbackWithSuccessTag finish_tag_; + + ::grpc_impl::experimental::CallbackServerContext* const ctx_; + ::grpc::internal::Call call_; + ::grpc::experimental::MessageHolder* const + allocator_state_; + std::function call_requester_; + // reactor_ can always be loaded/stored with relaxed memory ordering because + // its value is only set once, independently of other data in the object, + // and the loads that use it will always actually come provably later even + // though they are from different threads since they are triggered by + // actions initiated only by the setting up of the reactor_ variable. In + // a sense, it's a delayed "const": it gets its value from the SetupReactor + // method (not the constructor, so it's not a true const), but it doesn't + // change after that and it only gets used by actions caused, directly or + // indirectly, by that setup. This comment also applies to the reactor_ + // variables of the other streaming objects in this file. + std::atomic reactor_; + // callbacks_outstanding_ follows a refcount pattern + std::atomic callbacks_outstanding_{ + 3}; // reserve for start, Finish, and CompletionOp + }; +}; + +template +class CallbackClientStreamingHandler : public ::grpc::internal::MethodHandler { + public: + explicit CallbackClientStreamingHandler( + std::function*( + ::grpc_impl::experimental::CallbackServerContext*, ResponseType*)> + get_reactor) + : get_reactor_(std::move(get_reactor)) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a reader structure (that includes response) + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + + auto* reader = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackReaderImpl))) + ServerCallbackReaderImpl( + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + param.call, std::move(param.call_requester)); + param.server_context->BeginCompletionOp( + param.call, [reader](bool) { reader->MaybeDone(); }, reader); + + experimental::ServerReadReactor* reactor = nullptr; + if (param.status.ok()) { + reactor = ::grpc::internal::CatchingReactorGetter< + experimental::ServerReadReactor>( + get_reactor_, + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + reader->response()); + } + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(UnimplementedReadReactor))) + UnimplementedReadReactor( + ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } + + reader->SetupReactor(reactor); + } + + private: + std::function*( + ::grpc_impl::experimental::CallbackServerContext*, ResponseType*)> + get_reactor_; + + class ServerCallbackReaderImpl + : public experimental::ServerCallbackReader { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_, + false); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (s.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessagePtr(&resp_)); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + } + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + this->Ref(); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed) + ->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_, false); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Read(RequestType* req) override { + this->Ref(); + read_ops_.RecvMessage(req); + call_.PerformOps(&read_ops_); + } + + private: + friend class CallbackClientStreamingHandler; + + ServerCallbackReaderImpl( + ::grpc_impl::experimental::CallbackServerContext* ctx, + ::grpc::internal::Call* call, std::function call_requester) + : ctx_(ctx), call_(*call), call_requester_(std::move(call_requester)) {} + + void SetupReactor(experimental::ServerReadReactor* reactor) { + reactor_.store(reactor, std::memory_order_relaxed); + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed)->OnReadDone(ok); + MaybeDone(); + }, + &read_ops_, false); + read_ops_.set_core_cq_tag(&read_tag_); + this->BindReactor(reactor); + this->MaybeCallOnCancel(reactor); + this->MaybeDone(); + } + + ~ServerCallbackReaderImpl() {} + + ResponseType* response() { return &resp_; } + + void MaybeDone() override { + if (GPR_UNLIKELY(this->Unref() == 1)) { + reactor_.load(std::memory_order_relaxed)->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackReaderImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + ServerReactor* reactor() override { + return reactor_.load(std::memory_order_relaxed); + } + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallbackWithSuccessTag meta_tag_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; + ::grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::internal::CallOpSet< + ::grpc::internal::CallOpRecvMessage> + read_ops_; + ::grpc::internal::CallbackWithSuccessTag read_tag_; + + ::grpc_impl::experimental::CallbackServerContext* const ctx_; + ::grpc::internal::Call call_; + ResponseType resp_; + std::function call_requester_; + // The memory ordering of reactor_ follows ServerCallbackUnaryImpl. + std::atomic*> reactor_; + // callbacks_outstanding_ follows a refcount pattern + std::atomic callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +template +class CallbackServerStreamingHandler : public ::grpc::internal::MethodHandler { + public: + explicit CallbackServerStreamingHandler( + std::function*( + ::grpc_impl::experimental::CallbackServerContext*, + const RequestType*)> + get_reactor) + : get_reactor_(std::move(get_reactor)) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a writer structure + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + + auto* writer = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackWriterImpl))) + ServerCallbackWriterImpl( + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + param.call, static_cast(param.request), + std::move(param.call_requester)); + param.server_context->BeginCompletionOp( + param.call, [writer](bool) { writer->MaybeDone(); }, writer); + + experimental::ServerWriteReactor* reactor = nullptr; + if (param.status.ok()) { + reactor = ::grpc::internal::CatchingReactorGetter< + experimental::ServerWriteReactor>( + get_reactor_, + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + writer->request()); + } + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(UnimplementedWriteReactor))) + UnimplementedWriteReactor( + ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } + + writer->SetupReactor(reactor); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + ::grpc::Status* status, void** /*handler_data*/) final { + ::grpc::ByteBuffer buf; + buf.set_buffer(req); + auto* request = + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(RequestType))) RequestType(); + *status = + ::grpc::SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + request->~RequestType(); + return nullptr; + } + + private: + std::function*( + ::grpc_impl::experimental::CallbackServerContext*, const RequestType*)> + get_reactor_; + + class ServerCallbackWriterImpl + : public experimental::ServerCallbackWriter { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_, + false); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + this->Ref(); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed) + ->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_, false); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Write(const ResponseType* resp, + ::grpc::WriteOptions options) override { + this->Ref(); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + write_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WriteAndFinish(const ResponseType* resp, ::grpc::WriteOptions options, + ::grpc::Status s) override { + // This combines the write into the finish callback + // Don't send any message if the status is bad + if (s.ok()) { + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); + } + Finish(std::move(s)); + } + + private: + friend class CallbackServerStreamingHandler; + + ServerCallbackWriterImpl( + ::grpc_impl::experimental::CallbackServerContext* ctx, + ::grpc::internal::Call* call, const RequestType* req, + std::function call_requester) + : ctx_(ctx), + call_(*call), + req_(req), + call_requester_(std::move(call_requester)) {} + + void SetupReactor(experimental::ServerWriteReactor* reactor) { + reactor_.store(reactor, std::memory_order_relaxed); + write_tag_.Set( + call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed)->OnWriteDone(ok); + MaybeDone(); + }, + &write_ops_, false); + write_ops_.set_core_cq_tag(&write_tag_); + this->BindReactor(reactor); + this->MaybeCallOnCancel(reactor); + this->MaybeDone(); + } + ~ServerCallbackWriterImpl() { req_->~RequestType(); } + + const RequestType* request() { return req_; } + + void MaybeDone() override { + if (GPR_UNLIKELY(this->Unref() == 1)) { + reactor_.load(std::memory_order_relaxed)->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackWriterImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + ServerReactor* reactor() override { + return reactor_.load(std::memory_order_relaxed); + } + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallbackWithSuccessTag meta_tag_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; + ::grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage> + write_ops_; + ::grpc::internal::CallbackWithSuccessTag write_tag_; + + ::grpc_impl::experimental::CallbackServerContext* const ctx_; + ::grpc::internal::Call call_; + const RequestType* req_; + std::function call_requester_; + // The memory ordering of reactor_ follows ServerCallbackUnaryImpl. + std::atomic*> reactor_; + // callbacks_outstanding_ follows a refcount pattern + std::atomic callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +template +class CallbackBidiHandler : public ::grpc::internal::MethodHandler { + public: + explicit CallbackBidiHandler( + std::function*( + ::grpc_impl::experimental::CallbackServerContext*)> + get_reactor) + : get_reactor_(std::move(get_reactor)) {} + void RunHandler(const HandlerParameter& param) final { + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + + auto* stream = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackReaderWriterImpl))) + ServerCallbackReaderWriterImpl( + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context), + param.call, std::move(param.call_requester)); + param.server_context->BeginCompletionOp( + param.call, [stream](bool) { stream->MaybeDone(); }, stream); + + experimental::ServerBidiReactor* reactor = + nullptr; + if (param.status.ok()) { + reactor = ::grpc::internal::CatchingReactorGetter< + experimental::ServerBidiReactor>( + get_reactor_, + static_cast<::grpc_impl::experimental::CallbackServerContext*>( + param.server_context)); + } + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), + sizeof(UnimplementedBidiReactor))) + UnimplementedBidiReactor( + ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } + + stream->SetupReactor(reactor); + } + + private: + std::function*( + ::grpc_impl::experimental::CallbackServerContext*)> + get_reactor_; + + class ServerCallbackReaderWriterImpl + : public experimental::ServerCallbackReaderWriter { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_, + false); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + this->Ref(); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed) + ->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_, false); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Write(const ResponseType* resp, + ::grpc::WriteOptions options) override { + this->Ref(); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + write_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WriteAndFinish(const ResponseType* resp, ::grpc::WriteOptions options, + ::grpc::Status s) override { + // Don't send any message if the status is bad + if (s.ok()) { + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); + } + Finish(std::move(s)); + } + + void Read(RequestType* req) override { + this->Ref(); + read_ops_.RecvMessage(req); + call_.PerformOps(&read_ops_); + } + + private: + friend class CallbackBidiHandler; + + ServerCallbackReaderWriterImpl( + ::grpc_impl::experimental::CallbackServerContext* ctx, + ::grpc::internal::Call* call, std::function call_requester) + : ctx_(ctx), call_(*call), call_requester_(std::move(call_requester)) {} + + void SetupReactor( + experimental::ServerBidiReactor* reactor) { + reactor_.store(reactor, std::memory_order_relaxed); + write_tag_.Set( + call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed)->OnWriteDone(ok); + MaybeDone(); + }, + &write_ops_, false); + write_ops_.set_core_cq_tag(&write_tag_); + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_.load(std::memory_order_relaxed)->OnReadDone(ok); + MaybeDone(); + }, + &read_ops_, false); + read_ops_.set_core_cq_tag(&read_tag_); + this->BindReactor(reactor); + this->MaybeCallOnCancel(reactor); + this->MaybeDone(); + } + + void MaybeDone() override { + if (GPR_UNLIKELY(this->Unref() == 1)) { + reactor_.load(std::memory_order_relaxed)->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + ServerReactor* reactor() override { + return reactor_.load(std::memory_order_relaxed); + } + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallbackWithSuccessTag meta_tag_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; + ::grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage> + write_ops_; + ::grpc::internal::CallbackWithSuccessTag write_tag_; + ::grpc::internal::CallOpSet< + ::grpc::internal::CallOpRecvMessage> + read_ops_; + ::grpc::internal::CallbackWithSuccessTag read_tag_; + + ::grpc_impl::experimental::CallbackServerContext* const ctx_; + ::grpc::internal::Call call_; + std::function call_requester_; + // The memory ordering of reactor_ follows ServerCallbackUnaryImpl. + std::atomic*> + reactor_; + // callbacks_outstanding_ follows a refcount pattern + std::atomic callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +} // namespace internal +} // namespace grpc_impl + +#endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_HANDLERS_H diff --git a/include/grpcpp/impl/codegen/server_callback_impl.h b/include/grpcpp/impl/codegen/server_callback_impl.h index 052548c2236..4e899f51ec2 100644 --- a/include/grpcpp/impl/codegen/server_callback_impl.h +++ b/include/grpcpp/impl/codegen/server_callback_impl.h @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include namespace grpc_impl { @@ -39,6 +37,8 @@ namespace internal { // Forward declarations template +class CallbackUnaryHandler; +template class CallbackClientStreamingHandler; template class CallbackServerStreamingHandler; @@ -51,29 +51,69 @@ class ServerReactor { virtual void OnDone() = 0; virtual void OnCancel() = 0; + // The following is not API. It is for internal use only and specifies whether + // all reactions of this Reactor can be run without an extra executor + // scheduling. This should only be used for internally-defined reactors with + // trivial reactions. + virtual bool InternalInlineable() { return false; } + private: - friend class ::grpc_impl::ServerContext; + template + friend class CallbackUnaryHandler; template friend class CallbackClientStreamingHandler; template friend class CallbackServerStreamingHandler; template friend class CallbackBidiHandler; +}; - // The ServerReactor is responsible for tracking when it is safe to call - // OnCancel. This function should not be called until after OnStarted is done - // and the RPC has completed with a cancellation. This is tracked by counting - // how many of these conditions have been met and calling OnCancel when none - // remain unmet. +/// The base class of ServerCallbackUnary etc. +class ServerCallbackCall { + public: + virtual ~ServerCallbackCall() {} + + // This object is responsible for tracking when it is safe to call + // OnCancel. This function should not be called until after the method handler + // is done and the RPC has completed with a cancellation. This is tracked by + // counting how many of these conditions have been met and calling OnCancel + // when none remain unmet. - void MaybeCallOnCancel() { + // Fast version called with known reactor passed in, used from derived + // classes, typically in non-cancel case + void MaybeCallOnCancel(ServerReactor* reactor) { if (GPR_UNLIKELY(on_cancel_conditions_remaining_.fetch_sub( 1, std::memory_order_acq_rel) == 1)) { - OnCancel(); + CallOnCancel(reactor); } } - std::atomic on_cancel_conditions_remaining_{2}; + // Slower version called from object that doesn't know the reactor a priori + // (such as the ServerContext CompletionOp which is formed before the + // reactor). This is used in cancel cases only, so it's ok to be slower and + // invoke a virtual function. + void MaybeCallOnCancel() { MaybeCallOnCancel(reactor()); } + + protected: + /// Increases the reference count + void Ref() { callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); } + + /// Decreases the reference count and returns the previous value + int Unref() { + return callbacks_outstanding_.fetch_sub(1, std::memory_order_acq_rel); + } + + private: + virtual ServerReactor* reactor() = 0; + virtual void MaybeDone() = 0; + + // If the OnCancel reaction is inlineable, execute it inline. Otherwise send + // it to an executor. + void CallOnCancel(ServerReactor* reactor); + + std::atomic_int on_cancel_conditions_remaining_{2}; + std::atomic_int callbacks_outstanding_{ + 3}; // reserve for start, Finish, and CompletionOp }; template @@ -99,71 +139,34 @@ class DefaultMessageHolder namespace experimental { // Forward declarations -template +class ServerUnaryReactor; +template class ServerReadReactor; -template +template class ServerWriteReactor; template class ServerBidiReactor; -// For unary RPCs, the exposed controller class is only an interface -// and the actual implementation is an internal class. -class ServerCallbackRpcController { +// NOTE: The actual call/stream object classes are provided as API only to +// support mocking. There are no implementations of these class interfaces in +// the API. +class ServerCallbackUnary : public internal::ServerCallbackCall { public: - virtual ~ServerCallbackRpcController() = default; - - // The method handler must call this function when it is done so that - // the library knows to free its resources + virtual ~ServerCallbackUnary() {} virtual void Finish(::grpc::Status s) = 0; + virtual void SendInitialMetadata() = 0; - // Allow the method handler to push out the initial metadata before - // the response and status are ready - virtual void SendInitialMetadata(std::function) = 0; - - /// SetCancelCallback passes in a callback to be called when the RPC is - /// canceled for whatever reason (streaming calls have OnCancel instead). This - /// is an advanced and uncommon use with several important restrictions. This - /// function may not be called more than once on the same RPC. - /// - /// If code calls SetCancelCallback on an RPC, it must also call - /// ClearCancelCallback before calling Finish on the RPC controller. That - /// method makes sure that no cancellation callback is executed for this RPC - /// beyond the point of its return. ClearCancelCallback may be called even if - /// SetCancelCallback was not called for this RPC, and it may be called - /// multiple times. It _must_ be called if SetCancelCallback was called for - /// this RPC. - /// - /// The callback should generally be lightweight and nonblocking and primarily - /// concerned with clearing application state related to the RPC or causing - /// operations (such as cancellations) to happen on dependent RPCs. - /// - /// If the RPC is already canceled at the time that SetCancelCallback is - /// called, the callback is invoked immediately. - /// - /// The cancellation callback may be executed concurrently with the method - /// handler that invokes it but will certainly not issue or execute after the - /// return of ClearCancelCallback. If ClearCancelCallback is invoked while the - /// callback is already executing, the callback will complete its execution - /// before ClearCancelCallback takes effect. - /// - /// To preserve the orderings described above, the callback may be called - /// under a lock that is also used for ClearCancelCallback and - /// ServerContext::IsCancelled, so the callback CANNOT call either of those - /// operations on this RPC or any other function that causes those operations - /// to be called before the callback completes. - virtual void SetCancelCallback(std::function callback) = 0; - virtual void ClearCancelCallback() = 0; - - // NOTE: This is an API for advanced users who need custom allocators. - // Get and maybe mutate the allocator state associated with the current RPC. - virtual grpc::experimental::RpcAllocatorState* GetRpcAllocatorState() = 0; + protected: + // Use a template rather than explicitly specifying ServerUnaryReactor to + // delay binding and avoid a circular forward declaration issue + template + void BindReactor(Reactor* reactor) { + reactor->InternalBindCall(this); + } }; -// NOTE: The actual streaming object classes are provided -// as API only to support mocking. There are no implementations of -// these class interfaces in the API. template -class ServerCallbackReader { +class ServerCallbackReader : public internal::ServerCallbackCall { public: virtual ~ServerCallbackReader() {} virtual void Finish(::grpc::Status s) = 0; @@ -171,14 +174,13 @@ class ServerCallbackReader { virtual void Read(Request* msg) = 0; protected: - template - void BindReactor(ServerReadReactor* reactor) { + void BindReactor(ServerReadReactor* reactor) { reactor->InternalBindReader(this); } }; template -class ServerCallbackWriter { +class ServerCallbackWriter : public internal::ServerCallbackCall { public: virtual ~ServerCallbackWriter() {} @@ -186,21 +188,16 @@ class ServerCallbackWriter { virtual void SendInitialMetadata() = 0; virtual void Write(const Response* msg, ::grpc::WriteOptions options) = 0; virtual void WriteAndFinish(const Response* msg, ::grpc::WriteOptions options, - ::grpc::Status s) { - // Default implementation that can/should be overridden - Write(msg, std::move(options)); - Finish(std::move(s)); - } + ::grpc::Status s) = 0; protected: - template - void BindReactor(ServerWriteReactor* reactor) { + void BindReactor(ServerWriteReactor* reactor) { reactor->InternalBindWriter(this); } }; template -class ServerCallbackReaderWriter { +class ServerCallbackReaderWriter : public internal::ServerCallbackCall { public: virtual ~ServerCallbackReaderWriter() {} @@ -209,11 +206,7 @@ class ServerCallbackReaderWriter { virtual void Read(Request* msg) = 0; virtual void Write(const Response* msg, ::grpc::WriteOptions options) = 0; virtual void WriteAndFinish(const Response* msg, ::grpc::WriteOptions options, - ::grpc::Status s) { - // Default implementation that can/should be overridden - Write(msg, std::move(options)); - Finish(std::move(s)); - } + ::grpc::Status s) = 0; protected: void BindReactor(ServerBidiReactor* reactor) { @@ -222,34 +215,57 @@ class ServerCallbackReaderWriter { }; // The following classes are the reactor interfaces that are to be implemented -// by the user, returned as the result of the method handler for a callback -// method, and activated by the call to OnStarted. The library guarantees that -// OnStarted will be called for any reactor that has been created using a -// method handler registered on a service. No operation initiation method may be -// called until after the call to OnStarted. -// Note that none of the classes are pure; all reactions have a default empty -// reaction so that the user class only needs to override those classes that it -// cares about. +// by the user, returned as the output parameter of the method handler for a +// callback method. Note that none of the classes are pure; all reactions have a +// default empty reaction so that the user class only needs to override those +// classes that it cares about. /// \a ServerBidiReactor is the interface for a bidirectional streaming RPC. template class ServerBidiReactor : public internal::ServerReactor { public: + // NOTE: Initializing stream_ as a constructor initializer rather than a + // default initializer because gcc-4.x requires a copy constructor for + // default initializing a templated member, which isn't ok for atomic. + // TODO(vjpai): Switch to default constructor and default initializer when + // gcc-4.x is no longer supported + ServerBidiReactor() : stream_(nullptr) {} ~ServerBidiReactor() = default; - /// Do NOT call any operation initiation method (names that start with Start) - /// until after the library has called OnStarted on this object. - /// Send any initial metadata stored in the RPC context. If not invoked, /// any initial metadata will be passed along with the first Write or the /// Finish (if there are no writes). - void StartSendInitialMetadata() { stream_->SendInitialMetadata(); } + void StartSendInitialMetadata() { + ServerCallbackReaderWriter* stream = + stream_.load(std::memory_order_acquire); + if (stream == nullptr) { + grpc::internal::MutexLock l(&stream_mu_); + stream = stream_.load(std::memory_order_relaxed); + if (stream == nullptr) { + send_initial_metadata_wanted_ = true; + return; + } + } + stream->SendInitialMetadata(); + } /// Initiate a read operation. /// /// \param[out] req Where to eventually store the read message. Valid when /// the library calls OnReadDone - void StartRead(Request* req) { stream_->Read(req); } + void StartRead(Request* req) { + ServerCallbackReaderWriter* stream = + stream_.load(std::memory_order_acquire); + if (stream == nullptr) { + grpc::internal::MutexLock l(&stream_mu_); + stream = stream_.load(std::memory_order_relaxed); + if (stream == nullptr) { + read_wanted_ = req; + return; + } + } + stream->Read(req); + } /// Initiate a write operation. /// @@ -267,7 +283,18 @@ class ServerBidiReactor : public internal::ServerReactor { /// application regains ownership of resp. /// \param[in] options The WriteOptions to use for writing this message void StartWrite(const Response* resp, ::grpc::WriteOptions options) { - stream_->Write(resp, std::move(options)); + ServerCallbackReaderWriter* stream = + stream_.load(std::memory_order_acquire); + if (stream == nullptr) { + grpc::internal::MutexLock l(&stream_mu_); + stream = stream_.load(std::memory_order_relaxed); + if (stream == nullptr) { + write_wanted_ = resp; + write_options_wanted_ = std::move(options); + return; + } + } + stream->Write(resp, std::move(options)); } /// Initiate a write operation with specified options and final RPC Status, @@ -279,13 +306,26 @@ class ServerBidiReactor : public internal::ServerReactor { /// both. /// /// \param[in] resp The message to be written. The library takes temporary - /// ownership until Onone, at which point the application - /// regains ownership of resp. + /// ownership until OnWriteDone, at which point the + /// application regains ownership of resp. /// \param[in] options The WriteOptions to use for writing this message /// \param[in] s The status outcome of this RPC void StartWriteAndFinish(const Response* resp, ::grpc::WriteOptions options, ::grpc::Status s) { - stream_->WriteAndFinish(resp, std::move(options), std::move(s)); + ServerCallbackReaderWriter* stream = + stream_.load(std::memory_order_acquire); + if (stream == nullptr) { + grpc::internal::MutexLock l(&stream_mu_); + stream = stream_.load(std::memory_order_relaxed); + if (stream == nullptr) { + write_and_finish_wanted_ = true; + write_wanted_ = resp; + write_options_wanted_ = std::move(options); + status_wanted_ = std::move(s); + return; + } + } + stream->WriteAndFinish(resp, std::move(options), std::move(s)); } /// Inform system of a planned write operation with specified options, but @@ -306,15 +346,20 @@ class ServerBidiReactor : public internal::ServerReactor { /// cancelled. /// /// \param[in] s The status outcome of this RPC - void Finish(::grpc::Status s) { stream_->Finish(std::move(s)); } - - /// Notify the application that a streaming RPC has started and that it is now - /// ok to call any operation initiation method. An RPC is considered started - /// after the server has received all initial metadata from the client, which - /// is a result of the client calling StartCall(). - /// - /// \param[in] context The context object now associated with this RPC - virtual void OnStarted(::grpc_impl::ServerContext* /*context*/) {} + void Finish(::grpc::Status s) { + ServerCallbackReaderWriter* stream = + stream_.load(std::memory_order_acquire); + if (stream == nullptr) { + grpc::internal::MutexLock l(&stream_mu_); + stream = stream_.load(std::memory_order_relaxed); + if (stream == nullptr) { + finish_wanted_ = true; + status_wanted_ = std::move(s); + return; + } + } + stream->Finish(std::move(s)); + } /// Notifies the application that an explicit StartSendInitialMetadata /// operation completed. Not used when the sending of initial metadata @@ -338,9 +383,9 @@ class ServerBidiReactor : public internal::ServerReactor { virtual void OnWriteDone(bool /*ok*/) {} /// Notifies the application that all operations associated with this RPC - /// have completed. This is an override (from the internal base class) but not - /// final, so derived classes should override it if they want to take action. - void OnDone() override {} + /// have completed. This is an override (from the internal base class) but + /// still abstract, so derived classes MUST override it to be instantiated. + void OnDone() override = 0; /// Notifies the application that this RPC has been cancelled. This is an /// override (from the internal base class) but not final, so derived classes @@ -353,84 +398,219 @@ class ServerBidiReactor : public internal::ServerReactor { // customization point. virtual void InternalBindStream( ServerCallbackReaderWriter* stream) { - stream_ = stream; + grpc::internal::ReleasableMutexLock l(&stream_mu_); + stream_.store(stream, std::memory_order_release); + if (send_initial_metadata_wanted_) { + stream->SendInitialMetadata(); + send_initial_metadata_wanted_ = false; + } + if (read_wanted_ != nullptr) { + stream->Read(read_wanted_); + read_wanted_ = nullptr; + } + if (write_and_finish_wanted_) { + // Don't perform actual finish actions while holding lock since it could + // trigger OnDone that destroys this object including the still-held lock. + write_and_finish_wanted_ = false; + const Response* write_wanted = write_wanted_; + ::grpc::WriteOptions write_options_wanted = + std::move(write_options_wanted_); + ::grpc::Status status_wanted = std::move(status_wanted_); + l.Unlock(); + stream->WriteAndFinish(write_wanted, std::move(write_options_wanted), + std::move(status_wanted)); + return; + } else { + if (write_wanted_ != nullptr) { + stream->Write(write_wanted_, std::move(write_options_wanted_)); + write_wanted_ = nullptr; + } + if (finish_wanted_) { + finish_wanted_ = false; + ::grpc::Status status_wanted = std::move(status_wanted_); + l.Unlock(); + stream->Finish(std::move(status_wanted)); + return; + } + } } - ServerCallbackReaderWriter* stream_; + grpc::internal::Mutex stream_mu_; + std::atomic*> stream_; + bool send_initial_metadata_wanted_ /* GUARDED_BY(stream_mu_) */ = false; + bool write_and_finish_wanted_ /* GUARDED_BY(stream_mu_) */ = false; + bool finish_wanted_ /* GUARDED_BY(stream_mu_) */ = false; + Request* read_wanted_ /* GUARDED_BY(stream_mu_) */ = nullptr; + const Response* write_wanted_ /* GUARDED_BY(stream_mu_) */ = nullptr; + ::grpc::WriteOptions write_options_wanted_ /* GUARDED_BY(stream_mu_) */; + ::grpc::Status status_wanted_ /* GUARDED_BY(stream_mu_) */; }; /// \a ServerReadReactor is the interface for a client-streaming RPC. -template +template class ServerReadReactor : public internal::ServerReactor { public: + ServerReadReactor() : reader_(nullptr) {} ~ServerReadReactor() = default; /// The following operation initiations are exactly like ServerBidiReactor. - void StartSendInitialMetadata() { reader_->SendInitialMetadata(); } - void StartRead(Request* req) { reader_->Read(req); } - void Finish(::grpc::Status s) { reader_->Finish(std::move(s)); } - - /// Similar to ServerBidiReactor::OnStarted, except that this also provides - /// the response object that the stream fills in before calling Finish. - /// (It must be filled in if status is OK, but it may be filled in otherwise.) - /// - /// \param[in] context The context object now associated with this RPC - /// \param[in] resp The response object to be used by this RPC - virtual void OnStarted(::grpc_impl::ServerContext* /*context*/, - Response* /*resp*/) {} + void StartSendInitialMetadata() { + ServerCallbackReader* reader = + reader_.load(std::memory_order_acquire); + if (reader == nullptr) { + grpc::internal::MutexLock l(&reader_mu_); + reader = reader_.load(std::memory_order_relaxed); + if (reader == nullptr) { + send_initial_metadata_wanted_ = true; + return; + } + } + reader->SendInitialMetadata(); + } + void StartRead(Request* req) { + ServerCallbackReader* reader = + reader_.load(std::memory_order_acquire); + if (reader == nullptr) { + grpc::internal::MutexLock l(&reader_mu_); + reader = reader_.load(std::memory_order_relaxed); + if (reader == nullptr) { + read_wanted_ = req; + return; + } + } + reader->Read(req); + } + void Finish(::grpc::Status s) { + ServerCallbackReader* reader = + reader_.load(std::memory_order_acquire); + if (reader == nullptr) { + grpc::internal::MutexLock l(&reader_mu_); + reader = reader_.load(std::memory_order_relaxed); + if (reader == nullptr) { + finish_wanted_ = true; + status_wanted_ = std::move(s); + return; + } + } + reader->Finish(std::move(s)); + } /// The following notifications are exactly like ServerBidiReactor. virtual void OnSendInitialMetadataDone(bool /*ok*/) {} virtual void OnReadDone(bool /*ok*/) {} - void OnDone() override {} + void OnDone() override = 0; void OnCancel() override {} private: friend class ServerCallbackReader; + // May be overridden by internal implementation details. This is not a public // customization point. virtual void InternalBindReader(ServerCallbackReader* reader) { - reader_ = reader; + grpc::internal::ReleasableMutexLock l(&reader_mu_); + reader_.store(reader, std::memory_order_release); + if (send_initial_metadata_wanted_) { + reader->SendInitialMetadata(); + send_initial_metadata_wanted_ = false; + } + if (read_wanted_ != nullptr) { + reader->Read(read_wanted_); + read_wanted_ = nullptr; + } + if (finish_wanted_) { + finish_wanted_ = false; + ::grpc::Status status_wanted = std::move(status_wanted_); + l.Unlock(); + reader->Finish(std::move(status_wanted)); + return; + } } - ServerCallbackReader* reader_; + grpc::internal::Mutex reader_mu_; + std::atomic*> reader_; + bool send_initial_metadata_wanted_ /* GUARDED_BY(reader_mu_) */ = false; + bool finish_wanted_ /* GUARDED_BY(reader_mu_) */ = false; + Request* read_wanted_ /* GUARDED_BY(reader_mu_) */ = nullptr; + ::grpc::Status status_wanted_ /* GUARDED_BY(reader_mu_) */; }; /// \a ServerWriteReactor is the interface for a server-streaming RPC. -template +template class ServerWriteReactor : public internal::ServerReactor { public: + ServerWriteReactor() : writer_(nullptr) {} ~ServerWriteReactor() = default; /// The following operation initiations are exactly like ServerBidiReactor. - void StartSendInitialMetadata() { writer_->SendInitialMetadata(); } + void StartSendInitialMetadata() { + ServerCallbackWriter* writer = + writer_.load(std::memory_order_acquire); + if (writer == nullptr) { + grpc::internal::MutexLock l(&writer_mu_); + writer = writer_.load(std::memory_order_relaxed); + if (writer == nullptr) { + send_initial_metadata_wanted_ = true; + return; + } + } + writer->SendInitialMetadata(); + } void StartWrite(const Response* resp) { StartWrite(resp, ::grpc::WriteOptions()); } void StartWrite(const Response* resp, ::grpc::WriteOptions options) { - writer_->Write(resp, std::move(options)); + ServerCallbackWriter* writer = + writer_.load(std::memory_order_acquire); + if (writer == nullptr) { + grpc::internal::MutexLock l(&writer_mu_); + writer = writer_.load(std::memory_order_relaxed); + if (writer == nullptr) { + write_wanted_ = resp; + write_options_wanted_ = std::move(options); + return; + } + } + writer->Write(resp, std::move(options)); } void StartWriteAndFinish(const Response* resp, ::grpc::WriteOptions options, ::grpc::Status s) { - writer_->WriteAndFinish(resp, std::move(options), std::move(s)); + ServerCallbackWriter* writer = + writer_.load(std::memory_order_acquire); + if (writer == nullptr) { + grpc::internal::MutexLock l(&writer_mu_); + writer = writer_.load(std::memory_order_relaxed); + if (writer == nullptr) { + write_and_finish_wanted_ = true; + write_wanted_ = resp; + write_options_wanted_ = std::move(options); + status_wanted_ = std::move(s); + return; + } + } + writer->WriteAndFinish(resp, std::move(options), std::move(s)); } void StartWriteLast(const Response* resp, ::grpc::WriteOptions options) { StartWrite(resp, std::move(options.set_last_message())); } - void Finish(::grpc::Status s) { writer_->Finish(std::move(s)); } - - /// Similar to ServerBidiReactor::OnStarted, except that this also provides - /// the request object sent by the client. - /// - /// \param[in] context The context object now associated with this RPC - /// \param[in] req The request object sent by the client - virtual void OnStarted(::grpc_impl::ServerContext* /*context*/, - const Request* /*req*/) {} + void Finish(::grpc::Status s) { + ServerCallbackWriter* writer = + writer_.load(std::memory_order_acquire); + if (writer == nullptr) { + grpc::internal::MutexLock l(&writer_mu_); + writer = writer_.load(std::memory_order_relaxed); + if (writer == nullptr) { + finish_wanted_ = true; + status_wanted_ = std::move(s); + return; + } + } + writer->Finish(std::move(s)); + } /// The following notifications are exactly like ServerBidiReactor. virtual void OnSendInitialMetadataDone(bool /*ok*/) {} virtual void OnWriteDone(bool /*ok*/) {} - void OnDone() override {} + void OnDone() override = 0; void OnCancel() override {} private: @@ -438,750 +618,135 @@ class ServerWriteReactor : public internal::ServerReactor { // May be overridden by internal implementation details. This is not a public // customization point. virtual void InternalBindWriter(ServerCallbackWriter* writer) { - writer_ = writer; - } - - ServerCallbackWriter* writer_; -}; - -} // namespace experimental - -namespace internal { - -template -class UnimplementedReadReactor - : public experimental::ServerReadReactor { - public: - void OnDone() override { delete this; } - void OnStarted(::grpc_impl::ServerContext*, Response*) override { - this->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); - } -}; - -template -class UnimplementedWriteReactor - : public experimental::ServerWriteReactor { - public: - void OnDone() override { delete this; } - void OnStarted(::grpc_impl::ServerContext*, const Request*) override { - this->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); - } -}; - -template -class UnimplementedBidiReactor - : public experimental::ServerBidiReactor { - public: - void OnDone() override { delete this; } - void OnStarted(::grpc_impl::ServerContext*) override { - this->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); - } -}; - -template -class CallbackUnaryHandler : public grpc::internal::MethodHandler { - public: - CallbackUnaryHandler( - std::function - func) - : func_(func) {} - - void SetMessageAllocator( - ::grpc::experimental::MessageAllocator* - allocator) { - allocator_ = allocator; - } - - void RunHandler(const HandlerParameter& param) final { - // Arena allocate a controller structure (that includes request/response) - ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); - auto* allocator_state = static_cast< - grpc::experimental::MessageHolder*>( - param.internal_data); - auto* controller = - new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackRpcControllerImpl))) - ServerCallbackRpcControllerImpl(param.server_context, param.call, - allocator_state, - std::move(param.call_requester)); - ::grpc::Status status = param.status; - if (status.ok()) { - // Call the actual function handler and expect the user to call finish - grpc::internal::CatchingCallback(func_, param.server_context, - controller->request(), - controller->response(), controller); - } else { - // if deserialization failed, we need to fail the call - controller->Finish(status); - } - } - - void* Deserialize(grpc_call* call, grpc_byte_buffer* req, - ::grpc::Status* status, void** handler_data) final { - grpc::ByteBuffer buf; - buf.set_buffer(req); - RequestType* request = nullptr; - ::grpc::experimental::MessageHolder* - allocator_state = nullptr; - if (allocator_ != nullptr) { - allocator_state = allocator_->AllocateMessages(); + grpc::internal::ReleasableMutexLock l(&writer_mu_); + writer_.store(writer, std::memory_order_release); + if (send_initial_metadata_wanted_) { + writer->SendInitialMetadata(); + send_initial_metadata_wanted_ = false; + } + if (write_and_finish_wanted_) { + write_and_finish_wanted_ = false; + const Response* write_wanted = write_wanted_; + ::grpc::WriteOptions write_options_wanted = + std::move(write_options_wanted_); + ::grpc::Status status_wanted = std::move(status_wanted_); + l.Unlock(); + writer->WriteAndFinish(write_wanted, std::move(write_options_wanted), + std::move(status_wanted)); + return; } else { - allocator_state = - new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( - call, sizeof(DefaultMessageHolder))) - DefaultMessageHolder(); - } - *handler_data = allocator_state; - request = allocator_state->request(); - *status = - ::grpc::SerializationTraits::Deserialize(&buf, request); - buf.Release(); - if (status->ok()) { - return request; - } - // Clean up on deserialization failure. - allocator_state->Release(); - return nullptr; - } - - private: - std::function - func_; - grpc::experimental::MessageAllocator* allocator_ = - nullptr; - - // The implementation class of ServerCallbackRpcController is a private member - // of CallbackUnaryHandler since it is never exposed anywhere, and this allows - // it to take advantage of CallbackUnaryHandler's friendships. - class ServerCallbackRpcControllerImpl - : public experimental::ServerCallbackRpcController { - public: - void Finish(::grpc::Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // The response is dropped if the status is not OK. - if (s.ok()) { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, - finish_ops_.SendMessagePtr(response())); - } else { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + if (write_wanted_ != nullptr) { + writer->Write(write_wanted_, std::move(write_options_wanted_)); + write_wanted_ = nullptr; } - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata(std::function f) override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - // TODO(vjpai): Consider taking f as a move-capture if we adopt C++14 - // and if performance of this operation matters - meta_tag_.Set(call_.call(), - [this, f](bool ok) { - f(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - // Neither SetCancelCallback nor ClearCancelCallback should affect the - // callbacks_outstanding_ count since they are paired and both must precede - // the invocation of Finish (if they are used at all) - void SetCancelCallback(std::function callback) override { - ctx_->SetCancelCallback(std::move(callback)); - } - - void ClearCancelCallback() override { ctx_->ClearCancelCallback(); } - - grpc::experimental::RpcAllocatorState* GetRpcAllocatorState() override { - return allocator_state_; - } - - private: - friend class CallbackUnaryHandler; - - ServerCallbackRpcControllerImpl( - ServerContext* ctx, ::grpc::internal::Call* call, - ::grpc::experimental::MessageHolder* - allocator_state, - std::function call_requester) - : ctx_(ctx), - call_(*call), - allocator_state_(allocator_state), - call_requester_(std::move(call_requester)) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr); - } - - const RequestType* request() { return allocator_state_->request(); } - ResponseType* response() { return allocator_state_->response(); } - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - allocator_state_->Release(); - this->~ServerCallbackRpcControllerImpl(); // explicitly call destructor - ::grpc::g_core_codegen_interface->grpc_call_unref(call); - call_requester(); + if (finish_wanted_) { + finish_wanted_ = false; + ::grpc::Status status_wanted = std::move(status_wanted_); + l.Unlock(); + writer->Finish(std::move(status_wanted)); + return; } } + } - grpc::internal::CallOpSet - meta_ops_; - grpc::internal::CallbackWithSuccessTag meta_tag_; - grpc::internal::CallOpSet - finish_ops_; - grpc::internal::CallbackWithSuccessTag finish_tag_; - - ::grpc_impl::ServerContext* ctx_; - grpc::internal::Call call_; - grpc::experimental::MessageHolder* const - allocator_state_; - std::function call_requester_; - std::atomic callbacks_outstanding_{ - 2}; // reserve for Finish and CompletionOp - }; + grpc::internal::Mutex writer_mu_; + std::atomic*> writer_; + bool send_initial_metadata_wanted_ /* GUARDED_BY(writer_mu_) */ = false; + bool write_and_finish_wanted_ /* GUARDED_BY(writer_mu_) */ = false; + bool finish_wanted_ /* GUARDED_BY(writer_mu_) */ = false; + const Response* write_wanted_ /* GUARDED_BY(writer_mu_) */ = nullptr; + ::grpc::WriteOptions write_options_wanted_ /* GUARDED_BY(writer_mu_) */; + ::grpc::Status status_wanted_ /* GUARDED_BY(writer_mu_) */; }; -template -class CallbackClientStreamingHandler : public grpc::internal::MethodHandler { +class ServerUnaryReactor : public internal::ServerReactor { public: - CallbackClientStreamingHandler( - std::function< - experimental::ServerReadReactor*()> - func) - : func_(std::move(func)) {} - void RunHandler(const HandlerParameter& param) final { - // Arena allocate a reader structure (that includes response) - ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); - - experimental::ServerReadReactor* reactor = - param.status.ok() - ? ::grpc::internal::CatchingReactorCreator< - experimental::ServerReadReactor>( - func_) - : nullptr; - - if (reactor == nullptr) { - // if deserialization or reactor creator failed, we need to fail the call - reactor = new UnimplementedReadReactor; - } - - auto* reader = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackReaderImpl))) - ServerCallbackReaderImpl(param.server_context, param.call, - std::move(param.call_requester), reactor); - - reader->BindReactor(reactor); - reactor->OnStarted(param.server_context, reader->response()); - // The earliest that OnCancel can be called is after OnStarted is done. - reactor->MaybeCallOnCancel(); - reader->MaybeDone(); - } + ServerUnaryReactor() : call_(nullptr) {} + ~ServerUnaryReactor() = default; - private: - std::function*()> - func_; - - class ServerCallbackReaderImpl - : public experimental::ServerCallbackReader { - public: - void Finish(::grpc::Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // The response is dropped if the status is not OK. - if (s.ok()) { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, - finish_ops_.SendMessagePtr(&resp_)); - } else { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - } - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - meta_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnSendInitialMetadataDone(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); + /// The following operation initiations are exactly like ServerBidiReactor. + void StartSendInitialMetadata() { + ServerCallbackUnary* call = call_.load(std::memory_order_acquire); + if (call == nullptr) { + grpc::internal::MutexLock l(&call_mu_); + call = call_.load(std::memory_order_relaxed); + if (call == nullptr) { + send_initial_metadata_wanted_ = true; + return; } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - void Read(RequestType* req) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - read_ops_.RecvMessage(req); - call_.PerformOps(&read_ops_); - } - - private: - friend class CallbackClientStreamingHandler; - - ServerCallbackReaderImpl( - ::grpc_impl::ServerContext* ctx, grpc::internal::Call* call, - std::function call_requester, - experimental::ServerReadReactor* reactor) - : ctx_(ctx), - call_(*call), - call_requester_(std::move(call_requester)), - reactor_(reactor) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); - read_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadDone(ok); - MaybeDone(); - }, - &read_ops_); - read_ops_.set_core_cq_tag(&read_tag_); } - - ~ServerCallbackReaderImpl() {} - - ResponseType* response() { return &resp_; } - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - reactor_->OnDone(); - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - this->~ServerCallbackReaderImpl(); // explicitly call destructor - ::grpc::g_core_codegen_interface->grpc_call_unref(call); - call_requester(); + call->SendInitialMetadata(); + } + void Finish(::grpc::Status s) { + ServerCallbackUnary* call = call_.load(std::memory_order_acquire); + if (call == nullptr) { + grpc::internal::MutexLock l(&call_mu_); + call = call_.load(std::memory_order_relaxed); + if (call == nullptr) { + finish_wanted_ = true; + status_wanted_ = std::move(s); + return; } } - - grpc::internal::CallOpSet - meta_ops_; - grpc::internal::CallbackWithSuccessTag meta_tag_; - grpc::internal::CallOpSet - finish_ops_; - grpc::internal::CallbackWithSuccessTag finish_tag_; - grpc::internal::CallOpSet> - read_ops_; - grpc::internal::CallbackWithSuccessTag read_tag_; - - ::grpc_impl::ServerContext* ctx_; - grpc::internal::Call call_; - ResponseType resp_; - std::function call_requester_; - experimental::ServerReadReactor* reactor_; - std::atomic callbacks_outstanding_{ - 3}; // reserve for OnStarted, Finish, and CompletionOp - }; -}; - -template -class CallbackServerStreamingHandler : public grpc::internal::MethodHandler { - public: - CallbackServerStreamingHandler( - std::function< - experimental::ServerWriteReactor*()> - func) - : func_(std::move(func)) {} - void RunHandler(const HandlerParameter& param) final { - // Arena allocate a writer structure - ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); - - experimental::ServerWriteReactor* reactor = - param.status.ok() - ? ::grpc::internal::CatchingReactorCreator< - experimental::ServerWriteReactor>( - func_) - : nullptr; - - if (reactor == nullptr) { - // if deserialization or reactor creator failed, we need to fail the call - reactor = new UnimplementedWriteReactor; - } - - auto* writer = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackWriterImpl))) - ServerCallbackWriterImpl(param.server_context, param.call, - static_cast(param.request), - std::move(param.call_requester), reactor); - writer->BindReactor(reactor); - reactor->OnStarted(param.server_context, writer->request()); - // The earliest that OnCancel can be called is after OnStarted is done. - reactor->MaybeCallOnCancel(); - writer->MaybeDone(); + call->Finish(std::move(s)); } - void* Deserialize(grpc_call* call, grpc_byte_buffer* req, - ::grpc::Status* status, void** /*handler_data*/) final { - ::grpc::ByteBuffer buf; - buf.set_buffer(req); - auto* request = - new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( - call, sizeof(RequestType))) RequestType(); - *status = - ::grpc::SerializationTraits::Deserialize(&buf, request); - buf.Release(); - if (status->ok()) { - return request; - } - request->~RequestType(); - return nullptr; - } + /// The following notifications are exactly like ServerBidiReactor. + virtual void OnSendInitialMetadataDone(bool /*ok*/) {} + void OnDone() override = 0; + void OnCancel() override {} private: - std::function*()> - func_; - - class ServerCallbackWriterImpl - : public experimental::ServerCallbackWriter { - public: - void Finish(::grpc::Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - finish_ops_.set_core_cq_tag(&finish_tag_); - - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - meta_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnSendInitialMetadataDone(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - void Write(const ResponseType* resp, - ::grpc::WriteOptions options) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (options.is_last_message()) { - options.set_buffer_hint(); - } - if (!ctx_->sent_initial_metadata_) { - write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - write_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); - call_.PerformOps(&write_ops_); - } - - void WriteAndFinish(const ResponseType* resp, ::grpc::WriteOptions options, - ::grpc::Status s) override { - // This combines the write into the finish callback - // Don't send any message if the status is bad - if (s.ok()) { - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); - } - Finish(std::move(s)); - } - - private: - friend class CallbackServerStreamingHandler; - - ServerCallbackWriterImpl( - ::grpc_impl::ServerContext* ctx, grpc::internal::Call* call, - const RequestType* req, std::function call_requester, - experimental::ServerWriteReactor* reactor) - : ctx_(ctx), - call_(*call), - req_(req), - call_requester_(std::move(call_requester)), - reactor_(reactor) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); - write_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWriteDone(ok); - MaybeDone(); - }, - &write_ops_); - write_ops_.set_core_cq_tag(&write_tag_); - } - ~ServerCallbackWriterImpl() { req_->~RequestType(); } - - const RequestType* request() { return req_; } - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - reactor_->OnDone(); - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - this->~ServerCallbackWriterImpl(); // explicitly call destructor - ::grpc::g_core_codegen_interface->grpc_call_unref(call); - call_requester(); - } - } - - grpc::internal::CallOpSet - meta_ops_; - grpc::internal::CallbackWithSuccessTag meta_tag_; - grpc::internal::CallOpSet - finish_ops_; - grpc::internal::CallbackWithSuccessTag finish_tag_; - grpc::internal::CallOpSet - write_ops_; - grpc::internal::CallbackWithSuccessTag write_tag_; - - ::grpc_impl::ServerContext* ctx_; - grpc::internal::Call call_; - const RequestType* req_; - std::function call_requester_; - experimental::ServerWriteReactor* reactor_; - std::atomic callbacks_outstanding_{ - 3}; // reserve for OnStarted, Finish, and CompletionOp - }; -}; - -template -class CallbackBidiHandler : public grpc::internal::MethodHandler { - public: - CallbackBidiHandler( - std::function< - experimental::ServerBidiReactor*()> - func) - : func_(std::move(func)) {} - void RunHandler(const HandlerParameter& param) final { - ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); - - experimental::ServerBidiReactor* reactor = - param.status.ok() - ? ::grpc::internal::CatchingReactorCreator< - experimental::ServerBidiReactor>( - func_) - : nullptr; - - if (reactor == nullptr) { - // if deserialization or reactor creator failed, we need to fail the call - reactor = new UnimplementedBidiReactor; + friend class ServerCallbackUnary; + // May be overridden by internal implementation details. This is not a public + // customization point. + virtual void InternalBindCall(ServerCallbackUnary* call) { + grpc::internal::ReleasableMutexLock l(&call_mu_); + call_.store(call, std::memory_order_release); + if (send_initial_metadata_wanted_) { + call->SendInitialMetadata(); + send_initial_metadata_wanted_ = false; + } + if (finish_wanted_) { + finish_wanted_ = false; + ::grpc::Status status_wanted = std::move(status_wanted_); + l.Unlock(); + call->Finish(std::move(status_wanted)); + return; } - - auto* stream = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackReaderWriterImpl))) - ServerCallbackReaderWriterImpl(param.server_context, param.call, - std::move(param.call_requester), - reactor); - - stream->BindReactor(reactor); - reactor->OnStarted(param.server_context); - // The earliest that OnCancel can be called is after OnStarted is done. - reactor->MaybeCallOnCancel(); - stream->MaybeDone(); } - private: - std::function*()> - func_; - - class ServerCallbackReaderWriterImpl - : public experimental::ServerCallbackReaderWriter { - public: - void Finish(::grpc::Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - finish_ops_.set_core_cq_tag(&finish_tag_); - - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - meta_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnSendInitialMetadataDone(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - void Write(const ResponseType* resp, - ::grpc::WriteOptions options) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (options.is_last_message()) { - options.set_buffer_hint(); - } - if (!ctx_->sent_initial_metadata_) { - write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - write_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); - call_.PerformOps(&write_ops_); - } - - void WriteAndFinish(const ResponseType* resp, ::grpc::WriteOptions options, - ::grpc::Status s) override { - // Don't send any message if the status is bad - if (s.ok()) { - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); - } - Finish(std::move(s)); - } + grpc::internal::Mutex call_mu_; + std::atomic call_; + bool send_initial_metadata_wanted_ /* GUARDED_BY(writer_mu_) */ = false; + bool finish_wanted_ /* GUARDED_BY(writer_mu_) */ = false; + ::grpc::Status status_wanted_ /* GUARDED_BY(writer_mu_) */; +}; - void Read(RequestType* req) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - read_ops_.RecvMessage(req); - call_.PerformOps(&read_ops_); - } +} // namespace experimental - private: - friend class CallbackBidiHandler; - - ServerCallbackReaderWriterImpl( - ::grpc_impl::ServerContext* ctx, grpc::internal::Call* call, - std::function call_requester, - experimental::ServerBidiReactor* reactor) - : ctx_(ctx), - call_(*call), - call_requester_(std::move(call_requester)), - reactor_(reactor) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); - write_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWriteDone(ok); - MaybeDone(); - }, - &write_ops_); - write_ops_.set_core_cq_tag(&write_tag_); - read_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadDone(ok); - MaybeDone(); - }, - &read_ops_); - read_ops_.set_core_cq_tag(&read_tag_); - } - ~ServerCallbackReaderWriterImpl() {} - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - reactor_->OnDone(); - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor - ::grpc::g_core_codegen_interface->grpc_call_unref(call); - call_requester(); - } - } +namespace internal { - grpc::internal::CallOpSet - meta_ops_; - grpc::internal::CallbackWithSuccessTag meta_tag_; - grpc::internal::CallOpSet - finish_ops_; - grpc::internal::CallbackWithSuccessTag finish_tag_; - grpc::internal::CallOpSet - write_ops_; - grpc::internal::CallbackWithSuccessTag write_tag_; - grpc::internal::CallOpSet> - read_ops_; - grpc::internal::CallbackWithSuccessTag read_tag_; - - ::grpc_impl::ServerContext* ctx_; - grpc::internal::Call call_; - std::function call_requester_; - experimental::ServerBidiReactor* reactor_; - std::atomic callbacks_outstanding_{ - 3}; // reserve for OnStarted, Finish, and CompletionOp - }; +template +class FinishOnlyReactor : public Base { + public: + explicit FinishOnlyReactor(::grpc::Status s) { this->Finish(std::move(s)); } + void OnDone() override { this->~FinishOnlyReactor(); } }; -} // namespace internal +using UnimplementedUnaryReactor = + FinishOnlyReactor; +template +using UnimplementedReadReactor = + FinishOnlyReactor>; +template +using UnimplementedWriteReactor = + FinishOnlyReactor>; +template +using UnimplementedBidiReactor = + FinishOnlyReactor>; +} // namespace internal } // namespace grpc_impl #endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_IMPL_H diff --git a/include/grpcpp/impl/codegen/server_context.h b/include/grpcpp/impl/codegen/server_context.h index 7ae12755a1c..0976d8df923 100644 --- a/include/grpcpp/impl/codegen/server_context.h +++ b/include/grpcpp/impl/codegen/server_context.h @@ -22,7 +22,15 @@ #include namespace grpc { + typedef ::grpc_impl::ServerContext ServerContext; + +namespace experimental { + +typedef ::grpc_impl::experimental::ServerContextBase ServerContextBase; +typedef ::grpc_impl::experimental::CallbackServerContext CallbackServerContext; + +} // namespace experimental } // namespace grpc #endif // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H diff --git a/include/grpcpp/impl/codegen/server_context_impl.h b/include/grpcpp/impl/codegen/server_context_impl.h index 5dc38137860..47ac70e0fcb 100644 --- a/include/grpcpp/impl/codegen/server_context_impl.h +++ b/include/grpcpp/impl/codegen/server_context_impl.h @@ -18,6 +18,8 @@ #ifndef GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H #define GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H + +#include #include #include #include @@ -30,9 +32,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include @@ -72,6 +77,8 @@ template class ClientStreamingHandler; template class RpcMethodHandler; +template +class FinishOnlyReactor; template class ServerReaderWriterBody; template @@ -88,6 +95,10 @@ namespace grpc { class GenericServerContext; class ServerInterface; +namespace experimental { +class GenericCallbackServerContext; +} // namespace experimental + namespace internal { class Call; } // namespace internal @@ -95,29 +106,18 @@ class Call; namespace testing { class InteropServerContextInspector; class ServerContextTestSpouse; +class DefaultReactorTestPeer; } // namespace testing + } // namespace grpc namespace grpc_impl { -/// A ServerContext allows the person implementing a service handler to: -/// -/// - Add custom initial and trailing metadata key-value pairs that will -/// propagated to the client side. -/// - Control call settings such as compression and authentication. -/// - Access metadata coming from the client. -/// - Get performance metrics (ie, census). -/// -/// Context settings are only relevant to the call handler they are supplied to, -/// that is to say, they aren't sticky across multiple calls. Some of these -/// settings, such as the compression options, can be made persistent at server -/// construction time by specifying the appropriate \a ChannelArguments -/// to a \a grpc::ServerBuilder, via \a ServerBuilder::AddChannelArgument. -/// -/// \warning ServerContext instances should \em not be reused across rpcs. -class ServerContext { +namespace experimental { + +/// Base class of ServerContext. Experimental until callback API is final. +class ServerContextBase { public: - ServerContext(); // for async calls - ~ServerContext(); + virtual ~ServerContextBase(); /// Return the deadline for the server call. std::chrono::system_clock::time_point deadline() const { @@ -258,6 +258,11 @@ class ServerContext { /// Get the census context associated with this server call. const struct census_context* census_context() const; + /// Should be used for framework-level extensions only. + /// Applications never need to call this method. + grpc_call* c_call() { return call_; } + + protected: /// Async only. Has to be called before the rpc starts. /// Returns the tag in completion queue when the rpc finishes. /// IsCancelled() can then be called to check whether the rpc was cancelled. @@ -268,13 +273,44 @@ class ServerContext { async_notify_when_done_tag_ = tag; } - /// Should be used for framework-level extensions only. - /// Applications never need to call this method. - grpc_call* c_call() { return call_; } + /// NOTE: This is an API for advanced users who need custom allocators. + /// Get and maybe mutate the allocator state associated with the current RPC. + /// Currently only applicable for callback unary RPC methods. + /// WARNING: This is experimental API and could be changed or removed. + ::grpc::experimental::RpcAllocatorState* GetRpcAllocatorState() { + return message_allocator_state_; + } + + /// Get a library-owned default unary reactor for use in minimal reaction + /// cases. This supports typical unary RPC usage of providing a response and + /// status. It supports immediate Finish (finish from within the method + /// handler) or delayed Finish (finish called after the method handler + /// invocation). It does not support reacting to cancellation or completion, + /// or early sending of initial metadata. Since this is a library-owned + /// reactor, it should not be delete'd or freed in any way. This is more + /// efficient than creating a user-owned reactor both because of avoiding an + /// allocation and because its minimal reactions are optimized using a core + /// surface flag that allows their reactions to run inline without any + /// thread-hop. + /// + /// This method should not be called more than once or called after return + /// from the method handler. + /// + /// WARNING: This is experimental API and could be changed or removed. + ::grpc_impl::experimental::ServerUnaryReactor* DefaultReactor() { + auto reactor = &default_reactor_; + default_reactor_used_.store(true, std::memory_order_relaxed); + return reactor; + } + + /// Constructors for use by derived classes + ServerContextBase(); + ServerContextBase(gpr_timespec deadline, grpc_metadata_array* arr); private: friend class ::grpc::testing::InteropServerContextInspector; friend class ::grpc::testing::ServerContextTestSpouse; + friend class ::grpc::testing::DefaultReactorTestPeer; friend class ::grpc::ServerInterface; friend class ::grpc_impl::Server; template @@ -309,23 +345,24 @@ class ServerContext { friend class ::grpc_impl::internal::CallbackBidiHandler; template <::grpc::StatusCode code> friend class ::grpc_impl::internal::ErrorMethodHandler; + template + friend class ::grpc_impl::internal::FinishOnlyReactor; friend class ::grpc_impl::ClientContext; friend class ::grpc::GenericServerContext; + friend class ::grpc::experimental::GenericCallbackServerContext; /// Prevent copying. - ServerContext(const ServerContext&); - ServerContext& operator=(const ServerContext&); + ServerContextBase(const ServerContextBase&); + ServerContextBase& operator=(const ServerContextBase&); class CompletionOp; - void BeginCompletionOp(::grpc::internal::Call* call, - std::function callback, - ::grpc_impl::internal::ServerReactor* reactor); + void BeginCompletionOp( + ::grpc::internal::Call* call, std::function callback, + ::grpc_impl::internal::ServerCallbackCall* callback_controller); /// Return the tag queued by BeginCompletionOp() ::grpc::internal::CompletionQueueTag* GetCompletionOpTag(); - ServerContext(gpr_timespec deadline, grpc_metadata_array* arr); - void set_call(grpc_call* call) { call_ = call; } void BindDeadlineAndMetadata(gpr_timespec deadline, grpc_metadata_array* arr); @@ -336,9 +373,6 @@ class ServerContext { uint32_t initial_metadata_flags() const { return 0; } - void SetCancelCallback(std::function callback); - void ClearCancelCallback(); - ::grpc::experimental::ServerRpcInfo* set_server_rpc_info( const char* method, ::grpc::internal::RpcMethod::RpcType type, const std::vector func) { + test_unary_.reset(new TestServerCallbackUnary(this, std::move(func))); + } + bool test_status_set() const { + return (test_unary_ != nullptr) && test_unary_->status_set(); + } + ::grpc::Status test_status() const { return test_unary_->status(); } + + class TestServerCallbackUnary + : public ::grpc_impl::experimental::ServerCallbackUnary { + public: + TestServerCallbackUnary(ServerContextBase* ctx, + std::function func) + : reactor_(&ctx->default_reactor_), func_(std::move(func)) { + this->BindReactor(reactor_); + } + void Finish(::grpc::Status s) override { + status_ = s; + func_(std::move(s)); + status_set_.store(true, std::memory_order_release); + } + void SendInitialMetadata() override {} + + bool status_set() const { + return status_set_.load(std::memory_order_acquire); + } + ::grpc::Status status() const { return status_; } + + private: + void MaybeDone() override {} + ::grpc_impl::internal::ServerReactor* reactor() override { + return reactor_; + } + + ::grpc_impl::experimental::ServerUnaryReactor* const reactor_; + std::atomic_bool status_set_{false}; + ::grpc::Status status_; + const std::function func_; + }; + + Reactor default_reactor_; + std::atomic_bool default_reactor_used_{false}; + std::unique_ptr test_unary_; +}; + +} // namespace experimental + +/// A ServerContext or CallbackServerContext allows the code implementing a +/// service handler to: +/// +/// - Add custom initial and trailing metadata key-value pairs that will +/// propagated to the client side. +/// - Control call settings such as compression and authentication. +/// - Access metadata coming from the client. +/// - Get performance metrics (ie, census). +/// +/// Context settings are only relevant to the call handler they are supplied to, +/// that is to say, they aren't sticky across multiple calls. Some of these +/// settings, such as the compression options, can be made persistent at server +/// construction time by specifying the appropriate \a ChannelArguments +/// to a \a grpc::ServerBuilder, via \a ServerBuilder::AddChannelArgument. +/// +/// \warning ServerContext instances should \em not be reused across rpcs. +class ServerContext : public experimental::ServerContextBase { + public: + ServerContext() {} // for async calls + + using experimental::ServerContextBase::AddInitialMetadata; + using experimental::ServerContextBase::AddTrailingMetadata; + using experimental::ServerContextBase::IsCancelled; + using experimental::ServerContextBase::SetLoadReportingCosts; + using experimental::ServerContextBase::TryCancel; + using experimental::ServerContextBase::auth_context; + using experimental::ServerContextBase::c_call; + using experimental::ServerContextBase::census_context; + using experimental::ServerContextBase::client_metadata; + using experimental::ServerContextBase::compression_algorithm; + using experimental::ServerContextBase::compression_level; + using experimental::ServerContextBase::compression_level_set; + using experimental::ServerContextBase::deadline; + using experimental::ServerContextBase::peer; + using experimental::ServerContextBase::raw_deadline; + using experimental::ServerContextBase::set_compression_algorithm; + using experimental::ServerContextBase::set_compression_level; + + // Sync/CQ-based Async ServerContext only + using experimental::ServerContextBase::AsyncNotifyWhenDone; + + private: + // Constructor for internal use by server only + friend class ::grpc_impl::Server; + ServerContext(gpr_timespec deadline, grpc_metadata_array* arr) + : experimental::ServerContextBase(deadline, arr) {} + + // CallbackServerContext only + using experimental::ServerContextBase::DefaultReactor; + using experimental::ServerContextBase::GetRpcAllocatorState; + + /// Prevent copying. + ServerContext(const ServerContext&) = delete; + ServerContext& operator=(const ServerContext&) = delete; +}; + +namespace experimental { + +class CallbackServerContext : public ServerContextBase { + public: + /// Public constructors are for direct use only by mocking tests. In practice, + /// these objects will be owned by the library. + CallbackServerContext() {} + + using ServerContextBase::AddInitialMetadata; + using ServerContextBase::AddTrailingMetadata; + using ServerContextBase::IsCancelled; + using ServerContextBase::SetLoadReportingCosts; + using ServerContextBase::TryCancel; + using ServerContextBase::auth_context; + using ServerContextBase::c_call; + using ServerContextBase::census_context; + using ServerContextBase::client_metadata; + using ServerContextBase::compression_algorithm; + using ServerContextBase::compression_level; + using ServerContextBase::compression_level_set; + using ServerContextBase::deadline; + using ServerContextBase::peer; + using ServerContextBase::raw_deadline; + using ServerContextBase::set_compression_algorithm; + using ServerContextBase::set_compression_level; + + // CallbackServerContext only + using ServerContextBase::DefaultReactor; + using ServerContextBase::GetRpcAllocatorState; + + private: + // Sync/CQ-based Async ServerContext only + using ServerContextBase::AsyncNotifyWhenDone; + + /// Prevent copying. + CallbackServerContext(const CallbackServerContext&) = delete; + CallbackServerContext& operator=(const CallbackServerContext&) = delete; }; + +} // namespace experimental } // namespace grpc_impl + +static_assert(std::is_base_of<::grpc_impl::experimental::ServerContextBase, + ::grpc_impl::ServerContext>::value, + "improper base class"); +static_assert( + std::is_base_of<::grpc_impl::experimental::ServerContextBase, + ::grpc_impl::experimental::CallbackServerContext>::value, + "improper base class"); +static_assert(sizeof(::grpc_impl::experimental::ServerContextBase) == + sizeof(::grpc_impl::ServerContext), + "wrong size"); +static_assert(sizeof(::grpc_impl::experimental::ServerContextBase) == + sizeof(::grpc_impl::experimental::CallbackServerContext), + "wrong size"); + #endif // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H diff --git a/include/grpcpp/impl/codegen/server_interceptor.h b/include/grpcpp/impl/codegen/server_interceptor.h index dc1da1327af..66c4417aec1 100644 --- a/include/grpcpp/impl/codegen/server_interceptor.h +++ b/include/grpcpp/impl/codegen/server_interceptor.h @@ -27,8 +27,10 @@ #include namespace grpc_impl { -class ServerContext; +namespace experimental { +class ServerContextBase; } +} // namespace grpc_impl namespace grpc { @@ -80,7 +82,7 @@ class ServerRpcInfo { /// Return a pointer to the underlying ServerContext structure associated /// with the RPC to support features that apply to it - grpc_impl::ServerContext* server_context() { return ctx_; } + grpc_impl::experimental::ServerContextBase* server_context() { return ctx_; } private: static_assert(Type::UNARY == @@ -96,8 +98,8 @@ class ServerRpcInfo { static_cast(internal::RpcMethod::BIDI_STREAMING), "violated expectation about Type enum"); - ServerRpcInfo(grpc_impl::ServerContext* ctx, const char* method, - internal::RpcMethod::RpcType type) + ServerRpcInfo(grpc_impl::experimental::ServerContextBase* ctx, + const char* method, internal::RpcMethod::RpcType type) : ctx_(ctx), method_(method), type_(static_cast(type)) {} // Runs interceptor at pos \a pos. @@ -127,14 +129,14 @@ class ServerRpcInfo { } } - grpc_impl::ServerContext* ctx_ = nullptr; + grpc_impl::experimental::ServerContextBase* ctx_ = nullptr; const char* method_ = nullptr; const Type type_; std::atomic ref_{1}; std::vector> interceptors_; friend class internal::InterceptorBatchMethodsImpl; - friend class grpc_impl::ServerContext; + friend class grpc_impl::experimental::ServerContextBase; }; } // namespace experimental diff --git a/include/grpcpp/test/default_reactor_test_peer.h b/include/grpcpp/test/default_reactor_test_peer.h new file mode 100644 index 00000000000..6755c043333 --- /dev/null +++ b/include/grpcpp/test/default_reactor_test_peer.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_TEST_DEFAULT_REACTOR_TEST_PEER_H +#define GRPCPP_TEST_DEFAULT_REACTOR_TEST_PEER_H + +#include +#include + +namespace grpc { +namespace testing { + +/// A test-only class to monitor the behavior of the ServerContext's +/// DefaultReactor. It is intended for allow unit-testing of a callback API +/// service via direct invocation of the service methods rather than through +/// RPCs. It is only applicable for unary RPC methods that use the +/// DefaultReactor rather than any user-defined reactor. +class DefaultReactorTestPeer { + public: + explicit DefaultReactorTestPeer(experimental::CallbackServerContext* ctx) + : DefaultReactorTestPeer(ctx, [](::grpc::Status) {}) {} + DefaultReactorTestPeer(experimental::CallbackServerContext* ctx, + std::function finish_func) + : ctx_(ctx) { + ctx->SetupTestDefaultReactor(std::move(finish_func)); + } + ::grpc::experimental::ServerUnaryReactor* reactor() const { + return &ctx_->default_reactor_; + } + bool test_status_set() const { return ctx_->test_status_set(); } + Status test_status() const { return ctx_->test_status(); } + + private: + experimental::CallbackServerContext* const ctx_; // not owned +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPCPP_TEST_DEFAULT_REACTOR_TEST_PEER_H diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index 9da45831169..4e294961471 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -148,6 +148,7 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file, "grpcpp/impl/codegen/proto_utils.h", "grpcpp/impl/codegen/rpc_method.h", "grpcpp/impl/codegen/server_callback.h", + "grpcpp/impl/codegen/server_callback_handlers.h", "grpcpp/impl/codegen/server_context.h", "grpcpp/impl/codegen/service_type.h", "grpcpp/impl/codegen/status.h", @@ -922,14 +923,12 @@ void PrintHeaderServerCallbackMethodsHelper( " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); - printer->Print( - *vars, - "virtual void $Method$(" - "::grpc::ServerContext* /*context*/, const $RealRequest$* /*request*/, " - "$RealResponse$* /*response*/, " - "::grpc::experimental::ServerCallbackRpcController* " - "controller) { controller->Finish(::grpc::Status(" - "::grpc::StatusCode::UNIMPLEMENTED, \"\")); }\n"); + printer->Print(*vars, + "virtual ::grpc::experimental::ServerUnaryReactor* " + "$Method$(::grpc::experimental::CallbackServerContext* " + "/*context*/, const $RealRequest$* " + "/*request*/, $RealResponse$* /*response*/) { " + "return nullptr; }\n"); } else if (ClientOnlyStreaming(method)) { printer->Print( *vars, @@ -941,12 +940,12 @@ void PrintHeaderServerCallbackMethodsHelper( " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); - printer->Print( - *vars, - "virtual ::grpc::experimental::ServerReadReactor< " - "$RealRequest$, $RealResponse$>* $Method$() {\n" - " return new ::grpc_impl::internal::UnimplementedReadReactor<\n" - " $RealRequest$, $RealResponse$>;}\n"); + printer->Print(*vars, + "virtual ::grpc::experimental::ServerReadReactor< " + "$RealRequest$>* $Method$(" + "::grpc::experimental::CallbackServerContext* /*context*/, " + "$RealResponse$* /*response*/) { " + "return nullptr; }\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, @@ -961,9 +960,10 @@ void PrintHeaderServerCallbackMethodsHelper( printer->Print( *vars, "virtual ::grpc::experimental::ServerWriteReactor< " - "$RealRequest$, $RealResponse$>* $Method$() {\n" - " return new ::grpc_impl::internal::UnimplementedWriteReactor<\n" - " $RealRequest$, $RealResponse$>;}\n"); + "$RealResponse$>* " + "$Method$(::grpc::experimental::CallbackServerContext* /*context*/, " + "const $RealRequest$* /*request*/) { " + "return nullptr; }\n"); } else if (method->BidiStreaming()) { printer->Print( *vars, @@ -978,9 +978,9 @@ void PrintHeaderServerCallbackMethodsHelper( printer->Print( *vars, "virtual ::grpc::experimental::ServerBidiReactor< " - "$RealRequest$, $RealResponse$>* $Method$() {\n" - " return new ::grpc_impl::internal::UnimplementedBidiReactor<\n" - " $RealRequest$, $RealResponse$>;}\n"); + "$RealRequest$, $RealResponse$>* " + "$Method$(::grpc::experimental::CallbackServerContext* /*context*/) { " + "return nullptr; }\n"); } } @@ -1011,14 +1011,11 @@ void PrintHeaderServerMethodCallback( " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackUnaryHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this](::grpc::ServerContext* context,\n" - " const $RealRequest$* request,\n" - " $RealResponse$* response,\n" - " ::grpc::experimental::ServerCallbackRpcController* " - "controller) {\n" - " return this->$" - "Method$(context, request, response, controller);\n" - " }));\n}\n"); + " [this](::grpc::experimental::CallbackServerContext* context, " + "const $RealRequest$* " + "request, " + "$RealResponse$* response) { " + "return this->$Method$(context, request, response); }));}\n"); printer->Print(*vars, "void SetMessageAllocatorFor_$Method$(\n" " ::grpc::experimental::MessageAllocator< " @@ -1033,21 +1030,28 @@ void PrintHeaderServerMethodCallback( " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackClientStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this] { return this->$Method$(); }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context, " + "$RealResponse$* " + "response) { " + "return this->$Method$(context, response); }));\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackServerStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this] { return this->$Method$(); }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context, " + "const $RealRequest$* " + "request) { " + "return this->$Method$(context, request); }));\n"); } else if (method->BidiStreaming()) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackBidiHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this] { return this->$Method$(); }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context) { " + "return this->$Method$(context); }));\n"); } printer->Print(*vars, "}\n"); printer->Print(*vars, @@ -1086,35 +1090,39 @@ void PrintHeaderServerMethodRawCallback( " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackUnaryHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this](::grpc::ServerContext* context,\n" - " const $RealRequest$* request,\n" - " $RealResponse$* response,\n" - " ::grpc::experimental::ServerCallbackRpcController* " - "controller) {\n" - " this->$" - "Method$(context, request, response, controller);\n" - " }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context, " + "const $RealRequest$* " + "request, " + "$RealResponse$* response) { return " + "this->$Method$(context, request, response); }));\n"); } else if (ClientOnlyStreaming(method)) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackClientStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this] { return this->$Method$(); }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context, " + "$RealResponse$* response) " + "{ return this->$Method$(context, response); }));\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackServerStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this] { return this->$Method$(); }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context, " + "const" + "$RealRequest$* request) { return " + "this->$Method$(context, request); }));\n"); } else if (method->BidiStreaming()) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " new ::grpc_impl::internal::CallbackBidiHandler< " "$RealRequest$, $RealResponse$>(\n" - " [this] { return this->$Method$(); }));\n"); + " [this](::grpc::experimental::CallbackServerContext* context) { " + "return " + "this->$Method$(context); }));\n"); } printer->Print(*vars, "}\n"); printer->Print(*vars, @@ -1657,6 +1665,8 @@ grpc::string GetSourceIncludes(grpc_generator::File* file, "grpcpp/impl/codegen/method_handler.h", "grpcpp/impl/codegen/rpc_service_method.h", "grpcpp/impl/codegen/server_callback.h", + "grpcpp/impl/codegen/server_callback_handlers.h", + "grpcpp/impl/codegen/server_context.h", "grpcpp/impl/codegen/service_type.h", "grpcpp/impl/codegen/sync_stream.h"}; std::vector headers(headers_strs, array_end(headers_strs)); diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc index 9b29b54ab25..49d72b5e6d7 100644 --- a/src/core/lib/surface/completion_queue.cc +++ b/src/core/lib/surface/completion_queue.cc @@ -854,7 +854,8 @@ static void cq_end_op_for_callback( } auto* functor = static_cast(tag); - if (internal || grpc_iomgr_is_any_background_poller_thread()) { + if (internal || functor->inlineable || + grpc_iomgr_is_any_background_poller_thread()) { grpc_core::ApplicationCallbackExecCtx::Enqueue(functor, (error == GRPC_ERROR_NONE)); GRPC_ERROR_UNREF(error); diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc index d8f163c9762..f7ac2848425 100644 --- a/src/cpp/client/channel_cc.cc +++ b/src/cpp/client/channel_cc.cc @@ -213,7 +213,14 @@ bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed, namespace { class ShutdownCallback : public grpc_experimental_completion_queue_functor { public: - ShutdownCallback() { functor_run = &ShutdownCallback::Run; } + ShutdownCallback() { + functor_run = &ShutdownCallback::Run; + // Set inlineable to true since this callback is trivial and thus does not + // need to be run from the executor (triggering a thread hop). This should + // only be used by internal callbacks like this and not by user application + // code. + inlineable = true; + } // TakeCQ takes ownership of the cq into the shutdown callback // so that the shutdown callback will be responsible for destroying it void TakeCQ(::grpc::CompletionQueue* cq) { cq_ = cq; } diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc index 0aaaa5401ea..563c3e83a61 100644 --- a/src/cpp/client/client_context.cc +++ b/src/cpp/client/client_context.cc @@ -88,14 +88,27 @@ void ClientContext::set_credentials( } } -std::unique_ptr ClientContext::FromServerContext( - const grpc::ServerContext& context, PropagationOptions options) { +std::unique_ptr ClientContext::FromInternalServerContext( + const grpc_impl::experimental::ServerContextBase& context, + PropagationOptions options) { std::unique_ptr ctx(new ClientContext); ctx->propagate_from_call_ = context.call_; ctx->propagation_options_ = options; return ctx; } +std::unique_ptr ClientContext::FromServerContext( + const grpc_impl::ServerContext& server_context, + PropagationOptions options) { + return FromInternalServerContext(server_context, options); +} + +std::unique_ptr ClientContext::FromCallbackServerContext( + const grpc_impl::experimental::CallbackServerContext& server_context, + PropagationOptions options) { + return FromInternalServerContext(server_context, options); +} + void ClientContext::AddMetadata(const grpc::string& meta_key, const grpc::string& meta_value) { send_initial_metadata_.insert(std::make_pair(meta_key, meta_value)); diff --git a/src/cpp/server/server_callback.cc b/src/cpp/server/server_callback.cc new file mode 100644 index 00000000000..235c8ef4cfe --- /dev/null +++ b/src/cpp/server/server_callback.cc @@ -0,0 +1,52 @@ +/* + * Copyright 2019 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/executor.h" + +namespace grpc_impl { +namespace internal { + +void ServerCallbackCall::CallOnCancel(ServerReactor* reactor) { + if (reactor->InternalInlineable()) { + reactor->OnCancel(); + } else { + Ref(); + grpc_core::ExecCtx exec_ctx; + struct ClosureArg { + ServerCallbackCall* call; + ServerReactor* reactor; + }; + ClosureArg* arg = new ClosureArg{this, reactor}; + grpc_core::Executor::Run(GRPC_CLOSURE_CREATE( + [](void* void_arg, grpc_error*) { + ClosureArg* arg = + static_cast(void_arg); + arg->reactor->OnCancel(); + arg->call->MaybeDone(); + delete arg; + }, + arg, nullptr), + GRPC_ERROR_NONE); + } +} + +} // namespace internal +} // namespace grpc_impl diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc index 519b5b63238..64330d296d1 100644 --- a/src/cpp/server/server_cc.cc +++ b/src/cpp/server/server_cc.cc @@ -258,7 +258,14 @@ bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag, namespace { class ShutdownCallback : public grpc_experimental_completion_queue_functor { public: - ShutdownCallback() { functor_run = &ShutdownCallback::Run; } + ShutdownCallback() { + functor_run = &ShutdownCallback::Run; + // Set inlineable to true since this callback is trivial and thus does not + // need to be run from the executor (triggering a thread hop). This should + // only be used by internal callbacks like this and not by user application + // code. + inlineable = true; + } // TakeCQ takes ownership of the cq into the shutdown callback // so that the shutdown callback will be responsible for destroying it void TakeCQ(CompletionQueue* cq) { cq_ = cq; } @@ -536,8 +543,9 @@ class Server::CallbackRequestBase : public grpc::internal::CompletionQueueTag { template class Server::CallbackRequest final : public Server::CallbackRequestBase { public: - static_assert(std::is_base_of::value, - "ServerContextType must be derived from ServerContext"); + static_assert(std::is_base_of::value, + "ServerContextType must be derived from CallbackServerContext"); // The constructor needs to know the server for this callback request and its // index in the server's request count array to allow for proper dynamic @@ -609,6 +617,13 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase { CallbackCallTag(Server::CallbackRequest* req) : req_(req) { functor_run = &CallbackCallTag::StaticRun; + // Set inlineable to true since this callback is internally-controlled + // without taking any locks, and thus does not need to be run from the + // executor (which triggers a thread hop). This should only be used by + // internal callbacks like this and not by user application code. The work + // here is actually non-trivial, but there is no chance of having user + // locks conflict with each other so it's ok to run inlined. + inlineable = true; } // force_run can not be performed on a tag if operations using this tag @@ -784,14 +799,14 @@ class Server::CallbackRequest final : public Server::CallbackRequestBase { }; template <> -bool Server::CallbackRequest::FinalizeResult( - void** /*tag*/, bool* /*status*/) { +bool Server::CallbackRequest:: + FinalizeResult(void** /*tag*/, bool* /*status*/) { return false; } template <> -bool Server::CallbackRequest::FinalizeResult( - void** /*tag*/, bool* status) { +bool Server::CallbackRequest:: + FinalizeResult(void** /*tag*/, bool* status) { if (*status) { // TODO(yangg) remove the copy here ctx_.method_ = grpc::StringFromCopiedSlice(call_details_->method); @@ -803,13 +818,14 @@ bool Server::CallbackRequest::FinalizeResult( } template <> -const char* Server::CallbackRequest::method_name() const { +const char* Server::CallbackRequest< + grpc::experimental::CallbackServerContext>::method_name() const { return method_->name(); } template <> -const char* Server::CallbackRequest::method_name() - const { +const char* Server::CallbackRequest< + grpc::experimental::GenericCallbackServerContext>::method_name() const { return ctx_.method().c_str(); } @@ -1115,7 +1131,7 @@ bool Server::RegisterService(const grpc::string* host, grpc::Service* service) { // TODO(vjpai): Register these dynamically based on need for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) { callback_reqs_to_start_.push_back( - new CallbackRequest( + new CallbackRequest( this, method_index, method.get(), method_registration_tag)); } // Enqueue it so that it will be Request'ed later after all request @@ -1158,8 +1174,8 @@ void Server::RegisterCallbackGenericService( // TODO(vjpai): Register these dynamically based on need for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) { callback_reqs_to_start_.push_back( - new CallbackRequest(this, method_index, - nullptr, nullptr)); + new CallbackRequest( + this, method_index, nullptr, nullptr)); } } diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc index 35128fb07dc..983a51b5d49 100644 --- a/src/cpp/server/server_context.cc +++ b/src/cpp/server/server_context.cc @@ -36,17 +36,19 @@ #include "src/core/lib/surface/call.h" namespace grpc_impl { +namespace experimental { // CompletionOp -class ServerContext::CompletionOp final +class ServerContextBase::CompletionOp final : public ::grpc::internal::CallOpSetInterface { public: // initial refs: one in the server context, one in the cq // must ref the call before calling constructor and after deleting this - CompletionOp(::grpc::internal::Call* call, internal::ServerReactor* reactor) + CompletionOp(::grpc::internal::Call* call, + ::grpc_impl::internal::ServerCallbackCall* callback_controller) : call_(*call), - reactor_(reactor), + callback_controller_(callback_controller), has_tag_(false), tag_(nullptr), core_cq_tag_(this), @@ -99,22 +101,6 @@ class ServerContext::CompletionOp final tag_ = tag; } - void SetCancelCallback(std::function callback) { - grpc_core::MutexLock lock(&mu_); - - if (finalized_ && (cancelled_ != 0)) { - callback(); - return; - } - - cancel_callback_ = std::move(callback); - } - - void ClearCancelCallback() { - grpc_core::MutexLock g(&mu_); - cancel_callback_ = nullptr; - } - void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; } void* core_cq_tag() override { return core_cq_tag_; } @@ -152,7 +138,7 @@ class ServerContext::CompletionOp final } ::grpc::internal::Call call_; - internal::ServerReactor* const reactor_; + ::grpc_impl::internal::ServerCallbackCall* const callback_controller_; bool has_tag_; void* tag_; void* core_cq_tag_; @@ -160,12 +146,11 @@ class ServerContext::CompletionOp final grpc_core::Mutex mu_; bool finalized_; int cancelled_; // This is an int (not bool) because it is passed to core - std::function cancel_callback_; bool done_intercepting_; ::grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_; }; -void ServerContext::CompletionOp::Unref() { +void ServerContextBase::CompletionOp::Unref() { if (refs_.Unref()) { grpc_call* call = call_.call(); delete this; @@ -173,7 +158,7 @@ void ServerContext::CompletionOp::Unref() { } } -void ServerContext::CompletionOp::FillOps(::grpc::internal::Call* call) { +void ServerContextBase::CompletionOp::FillOps(::grpc::internal::Call* call) { grpc_op ops; ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER; ops.data.recv_close_on_server.cancelled = &cancelled_; @@ -189,7 +174,7 @@ void ServerContext::CompletionOp::FillOps(::grpc::internal::Call* call) { /* No interceptors to run here */ } -bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) { +bool ServerContextBase::CompletionOp::FinalizeResult(void** tag, bool* status) { bool ret = false; grpc_core::ReleasableMutexLock lock(&mu_); if (done_intercepting_) { @@ -213,21 +198,11 @@ bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) { // Decide whether to call the cancel callback before releasing the lock bool call_cancel = (cancelled_ != 0); - // If it's a unary cancel callback, call it under the lock so that it doesn't - // race with ClearCancelCallback. Although we don't normally call callbacks - // under a lock, this is a special case since the user needs a guarantee that - // the callback won't issue or run after ClearCancelCallback has returned. - // This requirement imposes certain restrictions on the callback, documented - // in the API comments of SetCancelCallback. - if (cancel_callback_) { - cancel_callback_(); - } - // Release the lock since we may call a callback and interceptors now. lock.Unlock(); - if (call_cancel && reactor_ != nullptr) { - reactor_->MaybeCallOnCancel(); + if (call_cancel && callback_controller_ != nullptr) { + callback_controller_->MaybeCallOnCancel(); } /* Add interception point and run through interceptors */ interceptor_methods_.AddInterceptionHookPoint( @@ -245,16 +220,19 @@ bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) { return false; } -// ServerContext body +// ServerContextBase body -ServerContext::ServerContext() { Setup(gpr_inf_future(GPR_CLOCK_REALTIME)); } +ServerContextBase::ServerContextBase() { + Setup(gpr_inf_future(GPR_CLOCK_REALTIME)); +} -ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata_array* arr) { +ServerContextBase::ServerContextBase(gpr_timespec deadline, + grpc_metadata_array* arr) { Setup(deadline); std::swap(*client_metadata_.arr(), *arr); } -void ServerContext::Setup(gpr_timespec deadline) { +void ServerContextBase::Setup(gpr_timespec deadline) { completion_op_ = nullptr; has_notify_when_done_tag_ = false; async_notify_when_done_tag_ = nullptr; @@ -267,15 +245,15 @@ void ServerContext::Setup(gpr_timespec deadline) { rpc_info_ = nullptr; } -void ServerContext::BindDeadlineAndMetadata(gpr_timespec deadline, - grpc_metadata_array* arr) { +void ServerContextBase::BindDeadlineAndMetadata(gpr_timespec deadline, + grpc_metadata_array* arr) { deadline_ = deadline; std::swap(*client_metadata_.arr(), *arr); } -ServerContext::~ServerContext() { Clear(); } +ServerContextBase::~ServerContextBase() { Clear(); } -void ServerContext::Clear() { +void ServerContextBase::Clear() { auth_context_.reset(); initial_metadata_.clear(); trailing_metadata_.clear(); @@ -294,11 +272,17 @@ void ServerContext::Clear() { call_ = nullptr; grpc_call_unref(call); } + if (default_reactor_used_.load(std::memory_order_relaxed)) { + default_reactor_.~Reactor(); + new (&default_reactor_) Reactor; + default_reactor_used_.store(false, std::memory_order_relaxed); + } + test_unary_.reset(); } -void ServerContext::BeginCompletionOp(::grpc::internal::Call* call, - std::function callback, - internal::ServerReactor* reactor) { +void ServerContextBase::BeginCompletionOp( + ::grpc::internal::Call* call, std::function callback, + ::grpc_impl::internal::ServerCallbackCall* callback_controller) { GPR_ASSERT(!completion_op_); if (rpc_info_) { rpc_info_->Ref(); @@ -306,9 +290,10 @@ void ServerContext::BeginCompletionOp(::grpc::internal::Call* call, grpc_call_ref(call->call()); completion_op_ = new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp))) - CompletionOp(call, reactor); - if (callback != nullptr) { - completion_tag_.Set(call->call(), std::move(callback), completion_op_); + CompletionOp(call, callback_controller); + if (callback_controller != nullptr) { + completion_tag_.Set(call->call(), std::move(callback), completion_op_, + true); completion_op_->set_core_cq_tag(&completion_tag_); completion_op_->set_tag(completion_op_); } else if (has_notify_when_done_tag_) { @@ -317,21 +302,21 @@ void ServerContext::BeginCompletionOp(::grpc::internal::Call* call, call->PerformOps(completion_op_); } -::grpc::internal::CompletionQueueTag* ServerContext::GetCompletionOpTag() { +::grpc::internal::CompletionQueueTag* ServerContextBase::GetCompletionOpTag() { return static_cast<::grpc::internal::CompletionQueueTag*>(completion_op_); } -void ServerContext::AddInitialMetadata(const grpc::string& key, - const grpc::string& value) { +void ServerContextBase::AddInitialMetadata(const grpc::string& key, + const grpc::string& value) { initial_metadata_.insert(std::make_pair(key, value)); } -void ServerContext::AddTrailingMetadata(const grpc::string& key, - const grpc::string& value) { +void ServerContextBase::AddTrailingMetadata(const grpc::string& key, + const grpc::string& value) { trailing_metadata_.insert(std::make_pair(key, value)); } -void ServerContext::TryCancel() const { +void ServerContextBase::TryCancel() const { ::grpc::internal::CancelInterceptorBatchMethods cancel_methods; if (rpc_info_) { for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) { @@ -345,15 +330,7 @@ void ServerContext::TryCancel() const { } } -void ServerContext::SetCancelCallback(std::function callback) { - completion_op_->SetCancelCallback(std::move(callback)); -} - -void ServerContext::ClearCancelCallback() { - completion_op_->ClearCancelCallback(); -} - -bool ServerContext::IsCancelled() const { +bool ServerContextBase::IsCancelled() const { if (completion_tag_) { // When using callback API, this result is always valid. return completion_op_->CheckCancelledAsync(); @@ -367,7 +344,7 @@ bool ServerContext::IsCancelled() const { } } -void ServerContext::set_compression_algorithm( +void ServerContextBase::set_compression_algorithm( grpc_compression_algorithm algorithm) { compression_algorithm_ = algorithm; const char* algorithm_name = nullptr; @@ -380,7 +357,7 @@ void ServerContext::set_compression_algorithm( AddInitialMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name); } -grpc::string ServerContext::peer() const { +grpc::string ServerContextBase::peer() const { grpc::string peer; if (call_) { char* c_peer = grpc_call_get_peer(call_); @@ -390,11 +367,11 @@ grpc::string ServerContext::peer() const { return peer; } -const struct census_context* ServerContext::census_context() const { +const struct census_context* ServerContextBase::census_context() const { return grpc_census_call_get_context(call_); } -void ServerContext::SetLoadReportingCosts( +void ServerContextBase::SetLoadReportingCosts( const std::vector& cost_data) { if (call_ == nullptr) return; for (const auto& cost_datum : cost_data) { @@ -402,4 +379,5 @@ void ServerContext::SetLoadReportingCosts( } } +} // namespace experimental } // namespace grpc_impl diff --git a/test/core/end2end/inproc_callback_test.cc b/test/core/end2end/inproc_callback_test.cc index 8d51421d4c0..60f2348c9df 100644 --- a/test/core/end2end/inproc_callback_test.cc +++ b/test/core/end2end/inproc_callback_test.cc @@ -41,6 +41,7 @@ class CQDeletingCallback : public grpc_experimental_completion_queue_functor { public: explicit CQDeletingCallback(F f) : func_(f) { functor_run = &CQDeletingCallback::Run; + inlineable = false; } ~CQDeletingCallback() {} static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { @@ -62,6 +63,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor { public: ShutdownCallback() : done_(false) { functor_run = &ShutdownCallback::StaticRun; + inlineable = false; gpr_mu_init(&mu_); gpr_cv_init(&cv_); } diff --git a/test/core/end2end/tests/connectivity.cc b/test/core/end2end/tests/connectivity.cc index c208178b613..1f589d11437 100644 --- a/test/core/end2end/tests/connectivity.cc +++ b/test/core/end2end/tests/connectivity.cc @@ -39,6 +39,7 @@ struct CallbackContext { explicit CallbackContext(void (*cb)( grpc_experimental_completion_queue_functor* functor, int success)) { functor.functor_run = cb; + functor.inlineable = false; gpr_event_init(&finished); } }; diff --git a/test/core/iomgr/threadpool_test.cc b/test/core/iomgr/threadpool_test.cc index 952cec34fea..ac6fc3fc44d 100644 --- a/test/core/iomgr/threadpool_test.cc +++ b/test/core/iomgr/threadpool_test.cc @@ -49,6 +49,7 @@ class SimpleFunctorForAdd : public grpc_experimental_completion_queue_functor { friend class SimpleFunctorCheckForAdd; SimpleFunctorForAdd() { functor_run = &SimpleFunctorForAdd::Run; + inlineable = true; internal_next = this; internal_success = 0; } @@ -142,6 +143,7 @@ class SimpleFunctorCheckForAdd public: SimpleFunctorCheckForAdd(int ok, int* count) : count_(count) { functor_run = &SimpleFunctorCheckForAdd::Run; + inlineable = true; internal_success = ok; } ~SimpleFunctorCheckForAdd() {} diff --git a/test/core/surface/completion_queue_test.cc b/test/core/surface/completion_queue_test.cc index f775cd28534..3d8304bc986 100644 --- a/test/core/surface/completion_queue_test.cc +++ b/test/core/surface/completion_queue_test.cc @@ -382,6 +382,7 @@ static void test_callback(void) { public: ShutdownCallback(bool* done) : done_(done) { functor_run = &ShutdownCallback::Run; + inlineable = false; } ~ShutdownCallback() {} static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { @@ -416,6 +417,8 @@ static void test_callback(void) { public: TagCallback(int* counter, int tag) : counter_(counter), tag_(tag) { functor_run = &TagCallback::Run; + // Inlineable should be false since this callback takes locks. + inlineable = false; } ~TagCallback() {} static void Run(grpc_experimental_completion_queue_functor* cb, diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden index aeb68d457cb..bd3b2a4c500 100644 --- a/test/cpp/codegen/compiler_test_golden +++ b/test/cpp/codegen/compiler_test_golden @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -338,13 +339,7 @@ class ServiceA final { ExperimentalWithCallbackMethod_MethodA1() { ::grpc::Service::experimental().MarkMethodCallback(0, new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( - [this](::grpc::ServerContext* context, - const ::grpc::testing::Request* request, - ::grpc::testing::Response* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->MethodA1(context, request, response, controller); - })); - } + [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodA1(context, request, response); }));} void SetMessageAllocatorFor_MethodA1( ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) { static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( @@ -359,7 +354,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual void MethodA1(::grpc::ServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } + virtual ::grpc::experimental::ServerUnaryReactor* MethodA1(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/) { return nullptr; } }; template class ExperimentalWithCallbackMethod_MethodA2 : public BaseClass { @@ -369,7 +364,7 @@ class ServiceA final { ExperimentalWithCallbackMethod_MethodA2() { ::grpc::Service::experimental().MarkMethodCallback(1, new ::grpc_impl::internal::CallbackClientStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( - [this] { return this->MethodA2(); })); + [this](::grpc::experimental::CallbackServerContext* context, ::grpc::testing::Response* response) { return this->MethodA2(context, response); })); } ~ExperimentalWithCallbackMethod_MethodA2() override { BaseClassMustBeDerivedFromService(this); @@ -379,9 +374,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual ::grpc::experimental::ServerReadReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA2() { - return new ::grpc_impl::internal::UnimplementedReadReactor< - ::grpc::testing::Request, ::grpc::testing::Response>;} + virtual ::grpc::experimental::ServerReadReactor< ::grpc::testing::Request>* MethodA2(::grpc::experimental::CallbackServerContext* /*context*/, ::grpc::testing::Response* /*response*/) { return nullptr; } }; template class ExperimentalWithCallbackMethod_MethodA3 : public BaseClass { @@ -391,7 +384,7 @@ class ServiceA final { ExperimentalWithCallbackMethod_MethodA3() { ::grpc::Service::experimental().MarkMethodCallback(2, new ::grpc_impl::internal::CallbackServerStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( - [this] { return this->MethodA3(); })); + [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::testing::Request* request) { return this->MethodA3(context, request); })); } ~ExperimentalWithCallbackMethod_MethodA3() override { BaseClassMustBeDerivedFromService(this); @@ -401,9 +394,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual ::grpc::experimental::ServerWriteReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA3() { - return new ::grpc_impl::internal::UnimplementedWriteReactor< - ::grpc::testing::Request, ::grpc::testing::Response>;} + virtual ::grpc::experimental::ServerWriteReactor< ::grpc::testing::Response>* MethodA3(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/) { return nullptr; } }; template class ExperimentalWithCallbackMethod_MethodA4 : public BaseClass { @@ -413,7 +404,7 @@ class ServiceA final { ExperimentalWithCallbackMethod_MethodA4() { ::grpc::Service::experimental().MarkMethodCallback(3, new ::grpc_impl::internal::CallbackBidiHandler< ::grpc::testing::Request, ::grpc::testing::Response>( - [this] { return this->MethodA4(); })); + [this](::grpc::experimental::CallbackServerContext* context) { return this->MethodA4(context); })); } ~ExperimentalWithCallbackMethod_MethodA4() override { BaseClassMustBeDerivedFromService(this); @@ -423,9 +414,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual ::grpc::experimental::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4() { - return new ::grpc_impl::internal::UnimplementedBidiReactor< - ::grpc::testing::Request, ::grpc::testing::Response>;} + virtual ::grpc::experimental::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4(::grpc::experimental::CallbackServerContext* /*context*/) { return nullptr; } }; typedef ExperimentalWithCallbackMethod_MethodA1 > > > ExperimentalCallbackService; template @@ -584,12 +573,7 @@ class ServiceA final { ExperimentalWithRawCallbackMethod_MethodA1() { ::grpc::Service::experimental().MarkMethodRawCallback(0, new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->MethodA1(context, request, response, controller); - })); + [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodA1(context, request, response); })); } ~ExperimentalWithRawCallbackMethod_MethodA1() override { BaseClassMustBeDerivedFromService(this); @@ -599,7 +583,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual void MethodA1(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } + virtual ::grpc::experimental::ServerUnaryReactor* MethodA1(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } }; template class ExperimentalWithRawCallbackMethod_MethodA2 : public BaseClass { @@ -609,7 +593,7 @@ class ServiceA final { ExperimentalWithRawCallbackMethod_MethodA2() { ::grpc::Service::experimental().MarkMethodRawCallback(1, new ::grpc_impl::internal::CallbackClientStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this] { return this->MethodA2(); })); + [this](::grpc::experimental::CallbackServerContext* context, ::grpc::ByteBuffer* response) { return this->MethodA2(context, response); })); } ~ExperimentalWithRawCallbackMethod_MethodA2() override { BaseClassMustBeDerivedFromService(this); @@ -619,9 +603,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual ::grpc::experimental::ServerReadReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA2() { - return new ::grpc_impl::internal::UnimplementedReadReactor< - ::grpc::ByteBuffer, ::grpc::ByteBuffer>;} + virtual ::grpc::experimental::ServerReadReactor< ::grpc::ByteBuffer>* MethodA2(::grpc::experimental::CallbackServerContext* /*context*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } }; template class ExperimentalWithRawCallbackMethod_MethodA3 : public BaseClass { @@ -631,7 +613,7 @@ class ServiceA final { ExperimentalWithRawCallbackMethod_MethodA3() { ::grpc::Service::experimental().MarkMethodRawCallback(2, new ::grpc_impl::internal::CallbackServerStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this] { return this->MethodA3(); })); + [this](::grpc::experimental::CallbackServerContext* context, const::grpc::ByteBuffer* request) { return this->MethodA3(context, request); })); } ~ExperimentalWithRawCallbackMethod_MethodA3() override { BaseClassMustBeDerivedFromService(this); @@ -641,9 +623,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual ::grpc::experimental::ServerWriteReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA3() { - return new ::grpc_impl::internal::UnimplementedWriteReactor< - ::grpc::ByteBuffer, ::grpc::ByteBuffer>;} + virtual ::grpc::experimental::ServerWriteReactor< ::grpc::ByteBuffer>* MethodA3(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/) { return nullptr; } }; template class ExperimentalWithRawCallbackMethod_MethodA4 : public BaseClass { @@ -653,7 +633,7 @@ class ServiceA final { ExperimentalWithRawCallbackMethod_MethodA4() { ::grpc::Service::experimental().MarkMethodRawCallback(3, new ::grpc_impl::internal::CallbackBidiHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this] { return this->MethodA4(); })); + [this](::grpc::experimental::CallbackServerContext* context) { return this->MethodA4(context); })); } ~ExperimentalWithRawCallbackMethod_MethodA4() override { BaseClassMustBeDerivedFromService(this); @@ -663,9 +643,7 @@ class ServiceA final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual ::grpc::experimental::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4() { - return new ::grpc_impl::internal::UnimplementedBidiReactor< - ::grpc::ByteBuffer, ::grpc::ByteBuffer>;} + virtual ::grpc::experimental::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4(::grpc::experimental::CallbackServerContext* /*context*/) { return nullptr; } }; template class WithStreamedUnaryMethod_MethodA1 : public BaseClass { @@ -816,13 +794,7 @@ class ServiceB final { ExperimentalWithCallbackMethod_MethodB1() { ::grpc::Service::experimental().MarkMethodCallback(0, new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( - [this](::grpc::ServerContext* context, - const ::grpc::testing::Request* request, - ::grpc::testing::Response* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->MethodB1(context, request, response, controller); - })); - } + [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodB1(context, request, response); }));} void SetMessageAllocatorFor_MethodB1( ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) { static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( @@ -837,7 +809,7 @@ class ServiceB final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual void MethodB1(::grpc::ServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } + virtual ::grpc::experimental::ServerUnaryReactor* MethodB1(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/, ::grpc::testing::Response* /*response*/) { return nullptr; } }; typedef ExperimentalWithCallbackMethod_MethodB1 ExperimentalCallbackService; template @@ -885,12 +857,7 @@ class ServiceB final { ExperimentalWithRawCallbackMethod_MethodB1() { ::grpc::Service::experimental().MarkMethodRawCallback(0, new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->MethodB1(context, request, response, controller); - })); + [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodB1(context, request, response); })); } ~ExperimentalWithRawCallbackMethod_MethodB1() override { BaseClassMustBeDerivedFromService(this); @@ -900,7 +867,7 @@ class ServiceB final { abort(); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } - virtual void MethodB1(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } + virtual ::grpc::experimental::ServerUnaryReactor* MethodB1(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; } }; template class WithStreamedUnaryMethod_MethodB1 : public BaseClass { diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index 52c8fe3fba3..1d84586e18e 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -1131,6 +1131,32 @@ TEST_P(End2endTest, CancelRpcBeforeStart) { } } +// TODO(https://github.com/grpc/grpc/issues/21263): stop using timed sleeps to +// synchronize cancellation semantics. +TEST_P(End2endTest, CancelDelayedRpc) { + MAYBE_SKIP_TEST; + ResetStub(); + EchoRequest request; + EchoResponse response; + ClientContext context; + request.set_message("hello"); + request.mutable_param()->set_server_sleep_us(100 * 1000); + request.mutable_param()->set_skip_cancelled_check(true); + Status s; + std::thread echo_thread([this, &s, &context, &request, &response] { + s = stub_->Echo(&context, request, &response); + EXPECT_EQ(StatusCode::CANCELLED, s.error_code()); + }); + std::this_thread::sleep_for(std::chrono::microseconds(10 * 1000)); + context.TryCancel(); + echo_thread.join(); + EXPECT_EQ("", response.message()); + EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code()); + if (GetParam().use_interceptors) { + EXPECT_EQ(20, DummyInterceptor::GetNumTimesCancel()); + } +} + // Client cancels request stream after sending two messages TEST_P(End2endTest, ClientCancelsRequestStream) { MAYBE_SKIP_TEST; @@ -1408,80 +1434,6 @@ TEST_P(End2endTest, ExpectErrorTest) { } } -TEST_P(End2endTest, DelayedRpcEarlyCanceledUsingCancelCallback) { - MAYBE_SKIP_TEST; - // This test case is only relevant with callback server. - // Additionally, using interceptors makes this test subject to - // timing-dependent failures if the interceptors take too long to run. - if (!GetParam().callback_server || GetParam().use_interceptors) { - return; - } - - ResetStub(); - ClientContext context; - context.AddMetadata(kServerUseCancelCallback, - grpc::to_string(MAYBE_USE_CALLBACK_EARLY_CANCEL)); - EchoRequest request; - EchoResponse response; - request.set_message("Hello"); - request.mutable_param()->set_skip_cancelled_check(true); - context.TryCancel(); - Status s = stub_->Echo(&context, request, &response); - EXPECT_EQ(StatusCode::CANCELLED, s.error_code()); -} - -TEST_P(End2endTest, DelayedRpcLateCanceledUsingCancelCallback) { - MAYBE_SKIP_TEST; - // This test case is only relevant with callback server. - // Additionally, using interceptors makes this test subject to - // timing-dependent failures if the interceptors take too long to run. - if (!GetParam().callback_server || GetParam().use_interceptors) { - return; - } - - ResetStub(); - ClientContext context; - context.AddMetadata(kServerUseCancelCallback, - grpc::to_string(MAYBE_USE_CALLBACK_LATE_CANCEL)); - EchoRequest request; - EchoResponse response; - request.set_message("Hello"); - request.mutable_param()->set_skip_cancelled_check(true); - // Let server sleep for 200 ms first to give the cancellation a chance. - // This is split into 100 ms to start the cancel and 100 ms extra time for - // it to make it to the server, to make it highly probable that the server - // RPC would have already started by the time the cancellation is sent - // and the server-side gets enough time to react to it. - request.mutable_param()->set_server_sleep_us(200000); - - std::thread echo_thread{[this, &context, &request, &response] { - Status s = stub_->Echo(&context, request, &response); - EXPECT_EQ(StatusCode::CANCELLED, s.error_code()); - }}; - std::this_thread::sleep_for(std::chrono::microseconds(100000)); - context.TryCancel(); - echo_thread.join(); -} - -TEST_P(End2endTest, DelayedRpcNonCanceledUsingCancelCallback) { - MAYBE_SKIP_TEST; - if (!GetParam().callback_server) { - return; - } - - ResetStub(); - EchoRequest request; - EchoResponse response; - request.set_message("Hello"); - - ClientContext context; - context.AddMetadata(kServerUseCancelCallback, - grpc::to_string(MAYBE_USE_CALLBACK_NO_CANCEL)); - - Status s = stub_->Echo(&context, request, &response); - EXPECT_TRUE(s.ok()); -} - ////////////////////////////////////////////////////////////////////////// // Test with and without a proxy. class ProxyEnd2endTest : public End2endTest { diff --git a/test/cpp/end2end/hybrid_end2end_test.cc b/test/cpp/end2end/hybrid_end2end_test.cc index 9eacb032f7b..0314ed251e2 100644 --- a/test/cpp/end2end/hybrid_end2end_test.cc +++ b/test/cpp/end2end/hybrid_end2end_test.cc @@ -809,14 +809,15 @@ TEST_P(HybridEnd2endTest, CallbackGenericEcho) { EchoTestService::WithGenericMethod_Echo service; class GenericEchoService : public experimental::CallbackGenericService { private: - experimental::ServerGenericBidiReactor* CreateReactor() override { + experimental::ServerGenericBidiReactor* CreateReactor( + experimental::GenericCallbackServerContext* context) override { + EXPECT_EQ(context->method(), "/grpc.testing.EchoTestService/Echo"); + class Reactor : public experimental::ServerGenericBidiReactor { + public: + Reactor() { StartRead(&request_); } + private: - void OnStarted(GenericServerContext* ctx) override { - ctx_ = ctx; - EXPECT_EQ(ctx->method(), "/grpc.testing.EchoTestService/Echo"); - StartRead(&request_); - } void OnDone() override { delete this; } void OnReadDone(bool ok) override { if (!ok) { @@ -832,7 +833,6 @@ TEST_P(HybridEnd2endTest, CallbackGenericEcho) { Finish(ok ? Status::OK : Status(StatusCode::UNKNOWN, "Unexpected failure")); } - GenericServerContext* ctx_; ByteBuffer request_; ByteBuffer response_; std::atomic_int reads_complete_{0}; diff --git a/test/cpp/end2end/message_allocator_end2end_test.cc b/test/cpp/end2end/message_allocator_end2end_test.cc index 05f68f521f5..312f182bdbf 100644 --- a/test/cpp/end2end/message_allocator_end2end_test.cc +++ b/test/cpp/end2end/message_allocator_end2end_test.cc @@ -71,14 +71,16 @@ class CallbackTestServiceImpl allocator_mutator_ = mutator; } - void Echo(ServerContext* /*context*/, const EchoRequest* request, - EchoResponse* response, - experimental::ServerCallbackRpcController* controller) override { + experimental::ServerUnaryReactor* Echo( + experimental::CallbackServerContext* context, const EchoRequest* request, + EchoResponse* response) override { response->set_message(request->message()); if (allocator_mutator_) { - allocator_mutator_(controller->GetRpcAllocatorState(), request, response); + allocator_mutator_(context->GetRpcAllocatorState(), request, response); } - controller->Finish(Status::OK); + auto* reactor = context->DefaultReactor(); + reactor->Finish(Status::OK); + return reactor; } private: diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc index cce4b11f07d..ff5d8d1193c 100644 --- a/test/cpp/end2end/mock_test.cc +++ b/test/cpp/end2end/mock_test.cc @@ -17,7 +17,6 @@ */ #include -#include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" @@ -50,6 +50,7 @@ using ::testing::SaveArg; using ::testing::SetArgPointee; using ::testing::WithArg; using ::testing::_; +using grpc::testing::DefaultReactorTestPeer; using grpc::testing::EchoRequest; using grpc::testing::EchoResponse; using grpc::testing::EchoTestService; @@ -161,6 +162,88 @@ class FakeClient { EchoTestService::StubInterface* stub_; }; +class CallbackTestServiceImpl + : public EchoTestService::ExperimentalCallbackService { + public: + experimental::ServerUnaryReactor* Echo( + experimental::CallbackServerContext* context, const EchoRequest* request, + EchoResponse* response) override { + // Make the mock service explicitly treat empty input messages as invalid + // arguments so that we can test various results of status. In general, a + // mocked service should just use the original service methods, but we are + // adding this variance in Status return value just to improve coverage in + // this test. + auto* reactor = context->DefaultReactor(); + if (request->message().length() > 0) { + response->set_message(request->message()); + reactor->Finish(Status::OK); + } else { + reactor->Finish(Status(StatusCode::INVALID_ARGUMENT, "Invalid request")); + } + return reactor; + } +}; + +class MockCallbackTest : public ::testing::Test { + protected: + CallbackTestServiceImpl service_; + ServerContext context_; +}; + +TEST_F(MockCallbackTest, MockedCallSucceedsWithWait) { + experimental::CallbackServerContext ctx; + EchoRequest req; + EchoResponse resp; + grpc::internal::Mutex mu; + grpc::internal::CondVar cv; + grpc::Status status; + bool status_set = false; + DefaultReactorTestPeer peer(&ctx, [&](::grpc::Status s) { + grpc::internal::MutexLock l(&mu); + status_set = true; + status = std::move(s); + cv.Signal(); + }); + + req.set_message("mock 1"); + auto* reactor = service_.Echo(&ctx, &req, &resp); + cv.WaitUntil(&mu, [&] { + grpc::internal::MutexLock l(&mu); + return status_set; + }); + EXPECT_EQ(reactor, peer.reactor()); + EXPECT_TRUE(peer.test_status_set()); + EXPECT_TRUE(peer.test_status().ok()); + EXPECT_TRUE(status_set); + EXPECT_TRUE(status.ok()); + EXPECT_EQ(req.message(), resp.message()); +} + +TEST_F(MockCallbackTest, MockedCallSucceeds) { + experimental::CallbackServerContext ctx; + EchoRequest req; + EchoResponse resp; + DefaultReactorTestPeer peer(&ctx); + + req.set_message("ha ha, consider yourself mocked."); + auto* reactor = service_.Echo(&ctx, &req, &resp); + EXPECT_EQ(reactor, peer.reactor()); + EXPECT_TRUE(peer.test_status_set()); + EXPECT_TRUE(peer.test_status().ok()); +} + +TEST_F(MockCallbackTest, MockedCallFails) { + experimental::CallbackServerContext ctx; + EchoRequest req; + EchoResponse resp; + DefaultReactorTestPeer peer(&ctx); + + auto* reactor = service_.Echo(&ctx, &req, &resp); + EXPECT_EQ(reactor, peer.reactor()); + EXPECT_TRUE(peer.test_status_set()); + EXPECT_EQ(peer.test_status().error_code(), StatusCode::INVALID_ARGUMENT); +} + class TestServiceImpl : public EchoTestService::Service { public: Status Echo(ServerContext* /*context*/, const EchoRequest* request, diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc index 20e4d2d4e93..c50f8d8b5f3 100644 --- a/test/cpp/end2end/test_service_impl.cc +++ b/test/cpp/end2end/test_service_impl.cc @@ -18,18 +18,17 @@ #include "test/cpp/end2end/test_service_impl.h" -#include -#include - #include #include #include +#include + +#include +#include #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "test/cpp/util/string_ref_helper.h" -#include - using std::chrono::system_clock; namespace grpc { @@ -38,8 +37,8 @@ namespace { // When echo_deadline is requested, deadline seen in the ServerContext is set in // the response in seconds. -void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request, - EchoResponse* response) { +void MaybeEchoDeadline(experimental::ServerContextBase* context, + const EchoRequest* request, EchoResponse* response) { if (request->has_param() && request->param().echo_deadline()) { gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME); if (context->deadline() != system_clock::time_point::max()) { @@ -50,7 +49,7 @@ void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request, } void CheckServerAuthContext( - const ServerContext* context, + const experimental::ServerContextBase* context, const grpc::string& expected_transport_security_type, const grpc::string& expected_client_identity) { std::shared_ptr auth_ctx = context->auth_context(); @@ -76,10 +75,9 @@ int MetadataMatchCount( const std::multimap& metadata, const grpc::string& key, const grpc::string& value) { int count = 0; - for (std::multimap::const_iterator iter = - metadata.begin(); - iter != metadata.end(); ++iter) { - if (ToString(iter->first) == key && ToString(iter->second) == value) { + for (const auto& metadatum : metadata) { + if (ToString(metadatum.first) == key && + ToString(metadatum.second) == value) { count++; } } @@ -119,26 +117,12 @@ void ServerTryCancel(ServerContext* context) { } } -void ServerTryCancelNonblocking(ServerContext* context) { +void ServerTryCancelNonblocking(experimental::CallbackServerContext* context) { EXPECT_FALSE(context->IsCancelled()); context->TryCancel(); gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request"); } -void LoopUntilCancelled(Alarm* alarm, ServerContext* context, - experimental::ServerCallbackRpcController* controller, - int loop_delay_us) { - if (!context->IsCancelled()) { - alarm->experimental().Set( - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), - gpr_time_from_micros(loop_delay_us, GPR_TIMESPAN)), - [alarm, context, controller, loop_delay_us](bool) { - LoopUntilCancelled(alarm, context, controller, loop_delay_us); - }); - } else { - controller->Finish(Status::CANCELLED); - } -} } // namespace Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request, @@ -203,22 +187,18 @@ Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request, if (request->has_param() && request->param().echo_metadata_initially()) { const std::multimap& client_metadata = context->client_metadata(); - for (std::multimap::const_iterator - iter = client_metadata.begin(); - iter != client_metadata.end(); ++iter) { - context->AddInitialMetadata(ToString(iter->first), - ToString(iter->second)); + for (const auto& metadatum : client_metadata) { + context->AddInitialMetadata(ToString(metadatum.first), + ToString(metadatum.second)); } } if (request->has_param() && request->param().echo_metadata()) { const std::multimap& client_metadata = context->client_metadata(); - for (std::multimap::const_iterator - iter = client_metadata.begin(); - iter != client_metadata.end(); ++iter) { - context->AddTrailingMetadata(ToString(iter->first), - ToString(iter->second)); + for (const auto& metadatum : client_metadata) { + context->AddTrailingMetadata(ToString(metadatum.first), + ToString(metadatum.second)); } // Terminate rpc with error and debug info in trailer. if (request->param().debug_info().stack_entries_size() || @@ -258,186 +238,6 @@ Status TestServiceImpl::CheckClientInitialMetadata( return Status::OK; } -void CallbackTestServiceImpl::Echo( - ServerContext* context, const EchoRequest* request, EchoResponse* response, - experimental::ServerCallbackRpcController* controller) { - CancelState* cancel_state = new CancelState; - int server_use_cancel_callback = - GetIntValueFromMetadata(kServerUseCancelCallback, - context->client_metadata(), DO_NOT_USE_CALLBACK); - if (server_use_cancel_callback != DO_NOT_USE_CALLBACK) { - controller->SetCancelCallback([cancel_state] { - EXPECT_FALSE(cancel_state->callback_invoked.exchange( - true, std::memory_order_relaxed)); - }); - if (server_use_cancel_callback == MAYBE_USE_CALLBACK_EARLY_CANCEL) { - EXPECT_TRUE(context->IsCancelled()); - EXPECT_TRUE( - cancel_state->callback_invoked.load(std::memory_order_relaxed)); - } else { - EXPECT_FALSE(context->IsCancelled()); - EXPECT_FALSE( - cancel_state->callback_invoked.load(std::memory_order_relaxed)); - } - } - // A bit of sleep to make sure that short deadline tests fail - if (request->has_param() && request->param().server_sleep_us() > 0) { - // Set an alarm for that much time - alarm_.experimental().Set( - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), - gpr_time_from_micros(request->param().server_sleep_us(), - GPR_TIMESPAN)), - [this, context, request, response, controller, cancel_state](bool) { - EchoNonDelayed(context, request, response, controller, cancel_state); - }); - } else { - EchoNonDelayed(context, request, response, controller, cancel_state); - } -} - -void CallbackTestServiceImpl::CheckClientInitialMetadata( - ServerContext* context, const SimpleRequest* /*request*/, - SimpleResponse* /*response*/, - experimental::ServerCallbackRpcController* controller) { - EXPECT_EQ(MetadataMatchCount(context->client_metadata(), - kCheckClientInitialMetadataKey, - kCheckClientInitialMetadataVal), - 1); - EXPECT_EQ(1u, - context->client_metadata().count(kCheckClientInitialMetadataKey)); - controller->Finish(Status::OK); -} - -void CallbackTestServiceImpl::EchoNonDelayed( - ServerContext* context, const EchoRequest* request, EchoResponse* response, - experimental::ServerCallbackRpcController* controller, - CancelState* cancel_state) { - int server_use_cancel_callback = - GetIntValueFromMetadata(kServerUseCancelCallback, - context->client_metadata(), DO_NOT_USE_CALLBACK); - - // Safe to clear cancel callback even if it wasn't set - controller->ClearCancelCallback(); - if (server_use_cancel_callback == MAYBE_USE_CALLBACK_EARLY_CANCEL || - server_use_cancel_callback == MAYBE_USE_CALLBACK_LATE_CANCEL) { - EXPECT_TRUE(context->IsCancelled()); - EXPECT_TRUE(cancel_state->callback_invoked.load(std::memory_order_relaxed)); - delete cancel_state; - controller->Finish(Status::CANCELLED); - return; - } - - EXPECT_FALSE(cancel_state->callback_invoked.load(std::memory_order_relaxed)); - delete cancel_state; - - if (request->has_param() && request->param().server_die()) { - gpr_log(GPR_ERROR, "The request should not reach application handler."); - GPR_ASSERT(0); - } - if (request->has_param() && request->param().has_expected_error()) { - const auto& error = request->param().expected_error(); - controller->Finish(Status(static_cast(error.code()), - error.error_message(), - error.binary_error_details())); - return; - } - int server_try_cancel = GetIntValueFromMetadata( - kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); - if (server_try_cancel > DO_NOT_CANCEL) { - // Since this is a unary RPC, by the time this server handler is called, - // the 'request' message is already read from the client. So the scenarios - // in server_try_cancel don't make much sense. Just cancel the RPC as long - // as server_try_cancel is not DO_NOT_CANCEL - EXPECT_FALSE(context->IsCancelled()); - context->TryCancel(); - gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request"); - - if (server_use_cancel_callback == DO_NOT_USE_CALLBACK) { - // Now wait until it's really canceled - LoopUntilCancelled(&alarm_, context, controller, 1000); - } - return; - } - - gpr_log(GPR_DEBUG, "Request message was %s", request->message().c_str()); - response->set_message(request->message()); - MaybeEchoDeadline(context, request, response); - if (host_) { - response->mutable_param()->set_host(*host_); - } - if (request->has_param() && request->param().client_cancel_after_us()) { - { - std::unique_lock lock(mu_); - signal_client_ = true; - } - if (server_use_cancel_callback == DO_NOT_USE_CALLBACK) { - // Now wait until it's really canceled - LoopUntilCancelled(&alarm_, context, controller, - request->param().client_cancel_after_us()); - } - return; - } else if (request->has_param() && - request->param().server_cancel_after_us()) { - alarm_.experimental().Set( - gpr_time_add( - gpr_now(GPR_CLOCK_REALTIME), - gpr_time_from_micros(request->param().server_cancel_after_us(), - GPR_TIMESPAN)), - [controller](bool) { controller->Finish(Status::CANCELLED); }); - return; - } else if (!request->has_param() || - !request->param().skip_cancelled_check()) { - EXPECT_FALSE(context->IsCancelled()); - } - - if (request->has_param() && request->param().echo_metadata_initially()) { - const std::multimap& client_metadata = - context->client_metadata(); - for (std::multimap::const_iterator - iter = client_metadata.begin(); - iter != client_metadata.end(); ++iter) { - context->AddInitialMetadata(ToString(iter->first), - ToString(iter->second)); - } - controller->SendInitialMetadata([](bool ok) { EXPECT_TRUE(ok); }); - } - - if (request->has_param() && request->param().echo_metadata()) { - const std::multimap& client_metadata = - context->client_metadata(); - for (std::multimap::const_iterator - iter = client_metadata.begin(); - iter != client_metadata.end(); ++iter) { - context->AddTrailingMetadata(ToString(iter->first), - ToString(iter->second)); - } - // Terminate rpc with error and debug info in trailer. - if (request->param().debug_info().stack_entries_size() || - !request->param().debug_info().detail().empty()) { - grpc::string serialized_debug_info = - request->param().debug_info().SerializeAsString(); - context->AddTrailingMetadata(kDebugInfoTrailerKey, serialized_debug_info); - controller->Finish(Status::CANCELLED); - return; - } - } - if (request->has_param() && - (request->param().expected_client_identity().length() > 0 || - request->param().check_auth_context())) { - CheckServerAuthContext(context, - request->param().expected_transport_security_type(), - request->param().expected_client_identity()); - } - if (request->has_param() && request->param().response_message_length() > 0) { - response->set_message( - grpc::string(request->param().response_message_length(), '\0')); - } - if (request->has_param() && request->param().echo_peer()) { - response->mutable_param()->set_peer(context->peer()); - } - controller->Finish(Status::OK); -} - // Unimplemented is left unimplemented to test the returned error. Status TestServiceImpl::RequestStream(ServerContext* context, @@ -605,47 +405,241 @@ Status TestServiceImpl::BidiStream( return Status::OK; } -experimental::ServerReadReactor* -CallbackTestServiceImpl::RequestStream() { - class Reactor : public ::grpc::experimental::ServerReadReactor { +experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo( + experimental::CallbackServerContext* context, const EchoRequest* request, + EchoResponse* response) { + class Reactor : public ::grpc::experimental::ServerUnaryReactor { public: - Reactor() {} - void OnStarted(ServerContext* context, EchoResponse* response) override { - // Assign ctx_ and response_ as late as possible to increase likelihood of - // catching any races + Reactor(CallbackTestServiceImpl* service, + experimental::CallbackServerContext* ctx, + const EchoRequest* request, EchoResponse* response) + : service_(service), ctx_(ctx), req_(request), resp_(response) { + // It should be safe to call IsCancelled here, even though we don't know + // the result. Call it asynchronously to see if we trigger any data races. + async_cancel_check_ = std::thread([this] { (void)ctx_->IsCancelled(); }); + + if (request->has_param() && request->param().server_sleep_us() > 0) { + // Set an alarm for that much time + alarm_.experimental().Set( + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_micros( + request->param().server_sleep_us(), GPR_TIMESPAN)), + [this](bool ok) { NonDelayed(ok); }); + } else { + NonDelayed(true); + } + started_ = true; + } + void OnSendInitialMetadataDone(bool ok) override { + EXPECT_TRUE(ok); + initial_metadata_sent_ = true; + } + void OnCancel() override { + EXPECT_TRUE(started_); + EXPECT_TRUE(ctx_->IsCancelled()); + // do the actual finish in the main handler only but use this as a chance + // to cancel any alarms. + alarm_.Cancel(); + on_cancel_invoked_ = true; + } + void OnDone() override { + if (req_->has_param() && req_->param().echo_metadata_initially()) { + EXPECT_TRUE(initial_metadata_sent_); + } + EXPECT_EQ(ctx_->IsCancelled(), on_cancel_invoked_); + async_cancel_check_.join(); + delete this; + } - // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by - // the server by calling ServerContext::TryCancel() depending on the - // value: - // CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server - // reads any message from the client CANCEL_DURING_PROCESSING: The RPC - // is cancelled while the server is reading messages from the client - // CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads - // all the messages from the client - server_try_cancel_ = GetIntValueFromMetadata( - kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); + private: + void NonDelayed(bool ok) { + if (!ok) { + EXPECT_TRUE(ctx_->IsCancelled()); + Finish(Status::CANCELLED); + return; + } + if (req_->has_param() && req_->param().server_die()) { + gpr_log(GPR_ERROR, "The request should not reach application handler."); + GPR_ASSERT(0); + } + if (req_->has_param() && req_->param().has_expected_error()) { + const auto& error = req_->param().expected_error(); + Finish(Status(static_cast(error.code()), + error.error_message(), error.binary_error_details())); + return; + } + int server_try_cancel = GetIntValueFromMetadata( + kServerTryCancelRequest, ctx_->client_metadata(), DO_NOT_CANCEL); + if (server_try_cancel != DO_NOT_CANCEL) { + // Since this is a unary RPC, by the time this server handler is called, + // the 'request' message is already read from the client. So the + // scenarios in server_try_cancel don't make much sense. Just cancel the + // RPC as long as server_try_cancel is not DO_NOT_CANCEL + EXPECT_FALSE(ctx_->IsCancelled()); + ctx_->TryCancel(); + gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request"); + LoopUntilCancelled(1000); + return; + } + gpr_log(GPR_DEBUG, "Request message was %s", req_->message().c_str()); + resp_->set_message(req_->message()); + MaybeEchoDeadline(ctx_, req_, resp_); + if (service_->host_) { + resp_->mutable_param()->set_host(*service_->host_); + } + if (req_->has_param() && req_->param().client_cancel_after_us()) { + { + std::unique_lock lock(service_->mu_); + service_->signal_client_ = true; + } + LoopUntilCancelled(req_->param().client_cancel_after_us()); + return; + } else if (req_->has_param() && req_->param().server_cancel_after_us()) { + alarm_.experimental().Set( + gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(req_->param().server_cancel_after_us(), + GPR_TIMESPAN)), + [this](bool) { Finish(Status::CANCELLED); }); + return; + } else if (!req_->has_param() || !req_->param().skip_cancelled_check()) { + EXPECT_FALSE(ctx_->IsCancelled()); + } - response->set_message(""); + if (req_->has_param() && req_->param().echo_metadata_initially()) { + const std::multimap& + client_metadata = ctx_->client_metadata(); + for (const auto& metadatum : client_metadata) { + ctx_->AddInitialMetadata(ToString(metadatum.first), + ToString(metadatum.second)); + } + StartSendInitialMetadata(); + } - if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) { - ServerTryCancelNonblocking(context); - ctx_ = context; - } else { - if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { - context->TryCancel(); - // Don't wait for it here + if (req_->has_param() && req_->param().echo_metadata()) { + const std::multimap& + client_metadata = ctx_->client_metadata(); + for (const auto& metadatum : client_metadata) { + ctx_->AddTrailingMetadata(ToString(metadatum.first), + ToString(metadatum.second)); } - ctx_ = context; - response_ = response; - StartRead(&request_); + // Terminate rpc with error and debug info in trailer. + if (req_->param().debug_info().stack_entries_size() || + !req_->param().debug_info().detail().empty()) { + grpc::string serialized_debug_info = + req_->param().debug_info().SerializeAsString(); + ctx_->AddTrailingMetadata(kDebugInfoTrailerKey, + serialized_debug_info); + Finish(Status::CANCELLED); + return; + } + } + if (req_->has_param() && + (req_->param().expected_client_identity().length() > 0 || + req_->param().check_auth_context())) { + CheckServerAuthContext(ctx_, + req_->param().expected_transport_security_type(), + req_->param().expected_client_identity()); } + if (req_->has_param() && req_->param().response_message_length() > 0) { + resp_->set_message( + grpc::string(req_->param().response_message_length(), '\0')); + } + if (req_->has_param() && req_->param().echo_peer()) { + resp_->mutable_param()->set_peer(ctx_->peer()); + } + Finish(Status::OK); + } + void LoopUntilCancelled(int loop_delay_us) { + if (!ctx_->IsCancelled()) { + alarm_.experimental().Set( + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(loop_delay_us, GPR_TIMESPAN)), + [this, loop_delay_us](bool ok) { + if (!ok) { + EXPECT_TRUE(ctx_->IsCancelled()); + } + LoopUntilCancelled(loop_delay_us); + }); + } else { + Finish(Status::CANCELLED); + } + } - on_started_done_ = true; + CallbackTestServiceImpl* const service_; + experimental::CallbackServerContext* const ctx_; + const EchoRequest* const req_; + EchoResponse* const resp_; + Alarm alarm_; + bool initial_metadata_sent_{false}; + bool started_{false}; + bool on_cancel_invoked_{false}; + std::thread async_cancel_check_; + }; + + return new Reactor(this, context, request, response); +} + +experimental::ServerUnaryReactor* +CallbackTestServiceImpl::CheckClientInitialMetadata( + experimental::CallbackServerContext* context, const SimpleRequest*, + SimpleResponse*) { + class Reactor : public ::grpc::experimental::ServerUnaryReactor { + public: + explicit Reactor(experimental::CallbackServerContext* ctx) { + EXPECT_EQ(MetadataMatchCount(ctx->client_metadata(), + kCheckClientInitialMetadataKey, + kCheckClientInitialMetadataVal), + 1); + EXPECT_EQ(ctx->client_metadata().count(kCheckClientInitialMetadataKey), + 1u); + Finish(Status::OK); + } + void OnDone() override { delete this; } + }; + + return new Reactor(context); +} + +experimental::ServerReadReactor* +CallbackTestServiceImpl::RequestStream( + experimental::CallbackServerContext* context, EchoResponse* response) { + // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by + // the server by calling ServerContext::TryCancel() depending on the + // value: + // CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server + // reads any message from the client CANCEL_DURING_PROCESSING: The RPC + // is cancelled while the server is reading messages from the client + // CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads + // all the messages from the client + int server_try_cancel = GetIntValueFromMetadata( + kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); + if (server_try_cancel == CANCEL_BEFORE_PROCESSING) { + ServerTryCancelNonblocking(context); + // Don't need to provide a reactor since the RPC is canceled + return nullptr; + } + + class Reactor : public ::grpc::experimental::ServerReadReactor { + public: + Reactor(experimental::CallbackServerContext* ctx, EchoResponse* response, + int server_try_cancel) + : ctx_(ctx), + response_(response), + server_try_cancel_(server_try_cancel) { + EXPECT_NE(server_try_cancel, CANCEL_BEFORE_PROCESSING); + response->set_message(""); + + if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { + ctx->TryCancel(); + // Don't wait for it here + } + StartRead(&request_); + setup_done_ = true; } void OnDone() override { delete this; } void OnCancel() override { - EXPECT_TRUE(on_started_done_); + EXPECT_TRUE(setup_done_); EXPECT_TRUE(ctx_->IsCancelled()); FinishOnce(Status::CANCELLED); } @@ -678,66 +672,62 @@ CallbackTestServiceImpl::RequestStream() { } } - ServerContext* ctx_; - EchoResponse* response_; + experimental::CallbackServerContext* const ctx_; + EchoResponse* const response_; EchoRequest request_; int num_msgs_read_{0}; int server_try_cancel_; std::mutex finish_mu_; bool finished_{false}; - bool on_started_done_{false}; + bool setup_done_{false}; }; - return new Reactor; + return new Reactor(context, response, server_try_cancel); } // Return 'kNumResponseStreamMsgs' messages. // TODO(yangg) make it generic by adding a parameter into EchoRequest -experimental::ServerWriteReactor* -CallbackTestServiceImpl::ResponseStream() { +experimental::ServerWriteReactor* +CallbackTestServiceImpl::ResponseStream( + experimental::CallbackServerContext* context, const EchoRequest* request) { + // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by + // the server by calling ServerContext::TryCancel() depending on the + // value: + // CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server + // reads any message from the client CANCEL_DURING_PROCESSING: The RPC + // is cancelled while the server is reading messages from the client + // CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads + // all the messages from the client + int server_try_cancel = GetIntValueFromMetadata( + kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); + if (server_try_cancel == CANCEL_BEFORE_PROCESSING) { + ServerTryCancelNonblocking(context); + } + class Reactor - : public ::grpc::experimental::ServerWriteReactor { + : public ::grpc::experimental::ServerWriteReactor { public: - Reactor() {} - void OnStarted(ServerContext* context, - const EchoRequest* request) override { - // Assign ctx_ and request_ as late as possible to increase likelihood of - // catching any races - - // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by - // the server by calling ServerContext::TryCancel() depending on the - // value: - // CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server - // reads any message from the client CANCEL_DURING_PROCESSING: The RPC - // is cancelled while the server is reading messages from the client - // CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads - // all the messages from the client - server_try_cancel_ = GetIntValueFromMetadata( - kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); + Reactor(experimental::CallbackServerContext* ctx, + const EchoRequest* request, int server_try_cancel) + : ctx_(ctx), request_(request), server_try_cancel_(server_try_cancel) { server_coalescing_api_ = GetIntValueFromMetadata( - kServerUseCoalescingApi, context->client_metadata(), 0); + kServerUseCoalescingApi, ctx->client_metadata(), 0); server_responses_to_send_ = GetIntValueFromMetadata( - kServerResponseStreamsToSend, context->client_metadata(), + kServerResponseStreamsToSend, ctx->client_metadata(), kServerDefaultResponseStreamsToSend); - if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) { - ServerTryCancelNonblocking(context); - ctx_ = context; - } else { - if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { - context->TryCancel(); - } - ctx_ = context; - request_ = request; + if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { + ctx->TryCancel(); + } + if (server_try_cancel_ != CANCEL_BEFORE_PROCESSING) { if (num_msgs_sent_ < server_responses_to_send_) { NextWrite(); } } - on_started_done_ = true; + setup_done_ = true; } void OnDone() override { delete this; } void OnCancel() override { - EXPECT_TRUE(on_started_done_); + EXPECT_TRUE(setup_done_); EXPECT_TRUE(ctx_->IsCancelled()); FinishOnce(Status::CANCELLED); } @@ -778,8 +768,8 @@ CallbackTestServiceImpl::ResponseStream() { StartWrite(&response_); } } - ServerContext* ctx_; - const EchoRequest* request_; + experimental::CallbackServerContext* const ctx_; + const EchoRequest* const request_; EchoResponse response_; int num_msgs_sent_{0}; int server_try_cancel_; @@ -787,21 +777,18 @@ CallbackTestServiceImpl::ResponseStream() { int server_responses_to_send_; std::mutex finish_mu_; bool finished_{false}; - bool on_started_done_{false}; + bool setup_done_{false}; }; - return new Reactor; + return new Reactor(context, request, server_try_cancel); } experimental::ServerBidiReactor* -CallbackTestServiceImpl::BidiStream() { +CallbackTestServiceImpl::BidiStream( + experimental::CallbackServerContext* context) { class Reactor : public ::grpc::experimental::ServerBidiReactor { public: - Reactor() {} - void OnStarted(ServerContext* context) override { - // Assign ctx_ as late as possible to increase likelihood of catching any - // races - + explicit Reactor(experimental::CallbackServerContext* ctx) : ctx_(ctx) { // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by // the server by calling ServerContext::TryCancel() depending on the // value: @@ -811,24 +798,22 @@ CallbackTestServiceImpl::BidiStream() { // CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads // all the messages from the client server_try_cancel_ = GetIntValueFromMetadata( - kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); - server_write_last_ = GetIntValueFromMetadata( - kServerFinishAfterNReads, context->client_metadata(), 0); + kServerTryCancelRequest, ctx->client_metadata(), DO_NOT_CANCEL); + server_write_last_ = GetIntValueFromMetadata(kServerFinishAfterNReads, + ctx->client_metadata(), 0); if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) { - ServerTryCancelNonblocking(context); - ctx_ = context; + ServerTryCancelNonblocking(ctx); } else { if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { - context->TryCancel(); + ctx->TryCancel(); } - ctx_ = context; StartRead(&request_); } - on_started_done_ = true; + setup_done_ = true; } void OnDone() override { delete this; } void OnCancel() override { - EXPECT_TRUE(on_started_done_); + EXPECT_TRUE(setup_done_); EXPECT_TRUE(ctx_->IsCancelled()); FinishOnce(Status::CANCELLED); } @@ -870,7 +855,7 @@ CallbackTestServiceImpl::BidiStream() { } } - ServerContext* ctx_; + experimental::CallbackServerContext* const ctx_; EchoRequest request_; EchoResponse response_; int num_msgs_read_{0}; @@ -878,10 +863,10 @@ CallbackTestServiceImpl::BidiStream() { int server_write_last_; std::mutex finish_mu_; bool finished_{false}; - bool on_started_done_{false}; + bool setup_done_{false}; }; - return new Reactor; + return new Reactor(context); } } // namespace testing diff --git a/test/cpp/end2end/test_service_impl.h b/test/cpp/end2end/test_service_impl.h index 81b0234e213..5673f0fa1c6 100644 --- a/test/cpp/end2end/test_service_impl.h +++ b/test/cpp/end2end/test_service_impl.h @@ -33,7 +33,6 @@ namespace testing { const int kServerDefaultResponseStreamsToSend = 3; const char* const kServerResponseStreamsToSend = "server_responses_to_send"; const char* const kServerTryCancelRequest = "server_try_cancel"; -const char* const kServerUseCancelCallback = "server_use_cancel_callback"; const char* const kDebugInfoTrailerKey = "debug-info-bin"; const char* const kServerFinishAfterNReads = "server_finish_after_n_reads"; const char* const kServerUseCoalescingApi = "server_use_coalescing_api"; @@ -47,13 +46,6 @@ typedef enum { CANCEL_AFTER_PROCESSING } ServerTryCancelRequestPhase; -typedef enum { - DO_NOT_USE_CALLBACK = 0, - MAYBE_USE_CALLBACK_EARLY_CANCEL, - MAYBE_USE_CALLBACK_LATE_CANCEL, - MAYBE_USE_CALLBACK_NO_CANCEL, -} ServerUseCancelCallback; - class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { public: TestServiceImpl() : signal_client_(false), host_() {} @@ -98,23 +90,24 @@ class CallbackTestServiceImpl explicit CallbackTestServiceImpl(const grpc::string& host) : signal_client_(false), host_(new grpc::string(host)) {} - void Echo(ServerContext* context, const EchoRequest* request, - EchoResponse* response, - experimental::ServerCallbackRpcController* controller) override; + experimental::ServerUnaryReactor* Echo( + experimental::CallbackServerContext* context, const EchoRequest* request, + EchoResponse* response) override; - void CheckClientInitialMetadata( - ServerContext* context, const SimpleRequest* request, - SimpleResponse* response, - experimental::ServerCallbackRpcController* controller) override; + experimental::ServerUnaryReactor* CheckClientInitialMetadata( + experimental::CallbackServerContext* context, const SimpleRequest*, + SimpleResponse*) override; - experimental::ServerReadReactor* RequestStream() - override; + experimental::ServerReadReactor* RequestStream( + experimental::CallbackServerContext* context, + EchoResponse* response) override; - experimental::ServerWriteReactor* ResponseStream() - override; + experimental::ServerWriteReactor* ResponseStream( + experimental::CallbackServerContext* context, + const EchoRequest* request) override; - experimental::ServerBidiReactor* BidiStream() - override; + experimental::ServerBidiReactor* BidiStream( + experimental::CallbackServerContext* context) override; // Unimplemented is left unimplemented to test the returned error. bool signal_client() { @@ -123,15 +116,6 @@ class CallbackTestServiceImpl } private: - struct CancelState { - std::atomic_bool callback_invoked{false}; - }; - void EchoNonDelayed(ServerContext* context, const EchoRequest* request, - EchoResponse* response, - experimental::ServerCallbackRpcController* controller, - CancelState* cancel_state); - - Alarm alarm_; bool signal_client_; std::mutex mu_; std::unique_ptr host_; diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc index 7a5f3f1738a..1e100ae56c0 100644 --- a/test/cpp/microbenchmarks/bm_cq.cc +++ b/test/cpp/microbenchmarks/bm_cq.cc @@ -160,6 +160,7 @@ class TagCallback : public grpc_experimental_completion_queue_functor { public: explicit TagCallback(int* iter) : iter_(iter) { functor_run = &TagCallback::Run; + inlineable = false; } ~TagCallback() {} static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { @@ -179,6 +180,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor { public: explicit ShutdownCallback(bool* done) : done_(done) { functor_run = &ShutdownCallback::Run; + inlineable = false; } ~ShutdownCallback() {} static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { diff --git a/test/cpp/microbenchmarks/bm_threadpool.cc b/test/cpp/microbenchmarks/bm_threadpool.cc index 9f813fa972c..e7e78346068 100644 --- a/test/cpp/microbenchmarks/bm_threadpool.cc +++ b/test/cpp/microbenchmarks/bm_threadpool.cc @@ -67,6 +67,7 @@ class AddAnotherFunctor : public grpc_experimental_completion_queue_functor { int num_add) : pool_(pool), counter_(counter), num_add_(num_add) { functor_run = &AddAnotherFunctor::Run; + inlineable = false; internal_next = this; internal_success = 0; } @@ -130,6 +131,7 @@ class SuicideFunctorForAdd : public grpc_experimental_completion_queue_functor { public: SuicideFunctorForAdd(BlockingCounter* counter) : counter_(counter) { functor_run = &SuicideFunctorForAdd::Run; + inlineable = false; internal_next = this; internal_success = 0; } @@ -182,6 +184,7 @@ class AddSelfFunctor : public grpc_experimental_completion_queue_functor { int num_add) : pool_(pool), counter_(counter), num_add_(num_add) { functor_run = &AddSelfFunctor::Run; + inlineable = false; internal_next = this; internal_success = 0; } @@ -261,6 +264,7 @@ class ShortWorkFunctorForAdd ShortWorkFunctorForAdd() { functor_run = &ShortWorkFunctorForAdd::Run; + inlineable = false; internal_next = this; internal_success = 0; val_ = 0; diff --git a/test/cpp/microbenchmarks/callback_test_service.cc b/test/cpp/microbenchmarks/callback_test_service.cc index a8829914bc6..b81f21b40ec 100644 --- a/test/cpp/microbenchmarks/callback_test_service.cc +++ b/test/cpp/microbenchmarks/callback_test_service.cc @@ -46,10 +46,9 @@ int GetIntValueFromMetadata( } } // namespace -void CallbackStreamingTestService::Echo( - ServerContext* context, const EchoRequest* /*request*/, - EchoResponse* response, - experimental::ServerCallbackRpcController* controller) { +experimental::ServerUnaryReactor* CallbackStreamingTestService::Echo( + experimental::CallbackServerContext* context, + const EchoRequest* /*request*/, EchoResponse* response) { int response_msgs_size = GetIntValueFromMetadata( kServerMessageSize, context->client_metadata(), 0); if (response_msgs_size > 0) { @@ -57,17 +56,18 @@ void CallbackStreamingTestService::Echo( } else { response->set_message(""); } - controller->Finish(Status::OK); + auto* reactor = context->DefaultReactor(); + reactor->Finish(::grpc::Status::OK); + return reactor; } experimental::ServerBidiReactor* -CallbackStreamingTestService::BidiStream() { +CallbackStreamingTestService::BidiStream( + experimental::CallbackServerContext* context) { class Reactor : public experimental::ServerBidiReactor { public: - Reactor() {} - void OnStarted(ServerContext* context) override { - ctx_ = context; + explicit Reactor(experimental::CallbackServerContext* context) { message_size_ = GetIntValueFromMetadata(kServerMessageSize, context->client_metadata(), 0); StartRead(&request_); @@ -100,14 +100,13 @@ CallbackStreamingTestService::BidiStream() { } private: - ServerContext* ctx_; EchoRequest request_; EchoResponse response_; int message_size_; bool finished_{false}; }; - return new Reactor; + return new Reactor(context); } } // namespace testing } // namespace grpc diff --git a/test/cpp/microbenchmarks/callback_test_service.h b/test/cpp/microbenchmarks/callback_test_service.h index 97188595382..f793d4887c1 100644 --- a/test/cpp/microbenchmarks/callback_test_service.h +++ b/test/cpp/microbenchmarks/callback_test_service.h @@ -36,12 +36,13 @@ class CallbackStreamingTestService : public EchoTestService::ExperimentalCallbackService { public: CallbackStreamingTestService() {} - void Echo(ServerContext* context, const EchoRequest* request, - EchoResponse* response, - experimental::ServerCallbackRpcController* controller) override; - experimental::ServerBidiReactor* BidiStream() - override; + experimental::ServerUnaryReactor* Echo( + experimental::CallbackServerContext* context, const EchoRequest* request, + EchoResponse* response) override; + + experimental::ServerBidiReactor* BidiStream( + experimental::CallbackServerContext* context) override; }; } // namespace testing } // namespace grpc diff --git a/test/cpp/qps/server_callback.cc b/test/cpp/qps/server_callback.cc index 9060f7368d6..4b6f651e356 100644 --- a/test/cpp/qps/server_callback.cc +++ b/test/cpp/qps/server_callback.cc @@ -33,25 +33,22 @@ namespace testing { class BenchmarkCallbackServiceImpl final : public BenchmarkService::ExperimentalCallbackService { public: - void UnaryCall( - ServerContext* /*context*/, const ::grpc::testing::SimpleRequest* request, - ::grpc::testing::SimpleResponse* response, - ::grpc::experimental::ServerCallbackRpcController* controller) override { - auto s = SetResponse(request, response); - controller->Finish(s); + ::grpc::experimental::ServerUnaryReactor* UnaryCall( + ::grpc::experimental::CallbackServerContext* context, + const SimpleRequest* request, SimpleResponse* response) override { + auto* reactor = context->DefaultReactor(); + reactor->Finish(SetResponse(request, response)); + return reactor; } ::grpc::experimental::ServerBidiReactor<::grpc::testing::SimpleRequest, ::grpc::testing::SimpleResponse>* - StreamingCall() override { + StreamingCall(::grpc::experimental::CallbackServerContext*) override { class Reactor : public ::grpc::experimental::ServerBidiReactor< ::grpc::testing::SimpleRequest, ::grpc::testing::SimpleResponse> { public: - Reactor() {} - void OnStarted(ServerContext* /*context*/) override { - StartRead(&request_); - } + Reactor() { StartRead(&request_); } void OnReadDone(bool ok) override { if (!ok) { diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++ index d4ce01ea629..eda8b94f491 100644 --- a/tools/doxygen/Doxyfile.c++ +++ b/tools/doxygen/Doxyfile.c++ @@ -990,6 +990,7 @@ include/grpcpp/impl/codegen/rpc_service_method.h \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ +include/grpcpp/impl/codegen/server_callback_handlers.h \ include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 1d69aa232d8..3e0efe82a88 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -992,6 +992,7 @@ include/grpcpp/impl/codegen/rpc_service_method.h \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ +include/grpcpp/impl/codegen/server_callback_handlers.h \ include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ @@ -1526,6 +1527,7 @@ src/cpp/server/insecure_server_credentials.cc \ src/cpp/server/secure_server_credentials.cc \ src/cpp/server/secure_server_credentials.h \ src/cpp/server/server_builder.cc \ +src/cpp/server/server_callback.cc \ src/cpp/server/server_cc.cc \ src/cpp/server/server_context.cc \ src/cpp/server/server_credentials.cc \