Merge pull request #18856 from vjpai/unary_reactor_server

Revamp server-side callback API and message allocator API
pull/21267/head
Vijay Pai 5 years ago committed by GitHub
commit 2c6a7e1f19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      BUILD
  2. 2
      BUILD.gn
  3. 6
      CMakeLists.txt
  4. 6
      Makefile
  5. 3
      build.yaml
  6. 2
      gRPC-C++.podspec
  7. 2
      grpc.gyp
  8. 4
      include/grpc/impl/codegen/grpc_types.h
  9. 68
      include/grpcpp/impl/codegen/async_generic_service.h
  10. 18
      include/grpcpp/impl/codegen/callback_common.h
  11. 28
      include/grpcpp/impl/codegen/client_callback_impl.h
  12. 13
      include/grpcpp/impl/codegen/client_context_impl.h
  13. 6
      include/grpcpp/impl/codegen/completion_queue_impl.h
  14. 37
      include/grpcpp/impl/codegen/method_handler_impl.h
  15. 11
      include/grpcpp/impl/codegen/rpc_service_method.h
  16. 12
      include/grpcpp/impl/codegen/server_callback.h
  17. 814
      include/grpcpp/impl/codegen/server_callback_handlers.h
  18. 1285
      include/grpcpp/impl/codegen/server_callback_impl.h
  19. 8
      include/grpcpp/impl/codegen/server_context.h
  20. 271
      include/grpcpp/impl/codegen/server_context_impl.h
  21. 14
      include/grpcpp/impl/codegen/server_interceptor.h
  22. 55
      include/grpcpp/test/default_reactor_test_peer.h
  23. 94
      src/compiler/cpp_generator.cc
  24. 3
      src/core/lib/surface/completion_queue.cc
  25. 9
      src/cpp/client/channel_cc.cc
  26. 17
      src/cpp/client/client_context.cc
  27. 52
      src/cpp/server/server_callback.cc
  28. 42
      src/cpp/server/server_cc.cc
  29. 116
      src/cpp/server/server_context.cc
  30. 2
      test/core/end2end/inproc_callback_test.cc
  31. 1
      test/core/end2end/tests/connectivity.cc
  32. 2
      test/core/iomgr/threadpool_test.cc
  33. 3
      test/core/surface/completion_queue_test.cc
  34. 75
      test/cpp/codegen/compiler_test_golden
  35. 100
      test/cpp/end2end/end2end_test.cc
  36. 14
      test/cpp/end2end/hybrid_end2end_test.cc
  37. 12
      test/cpp/end2end/message_allocator_end2end_test.cc
  38. 85
      test/cpp/end2end/mock_test.cc
  39. 599
      test/cpp/end2end/test_service_impl.cc
  40. 44
      test/cpp/end2end/test_service_impl.h
  41. 2
      test/cpp/microbenchmarks/bm_cq.cc
  42. 4
      test/cpp/microbenchmarks/bm_threadpool.cc
  43. 21
      test/cpp/microbenchmarks/callback_test_service.cc
  44. 11
      test/cpp/microbenchmarks/callback_test_service.h
  45. 19
      test/cpp/qps/server_callback.cc
  46. 1
      tools/doxygen/Doxyfile.c++
  47. 2
      tools/doxygen/Doxyfile.c++.internal

@ -150,6 +150,7 @@ GRPCXX_SRCS = [
"src/cpp/server/health/health_check_service.cc", "src/cpp/server/health/health_check_service.cc",
"src/cpp/server/health/health_check_service_server_builder_option.cc", "src/cpp/server/health/health_check_service_server_builder_option.cc",
"src/cpp/server/server_builder.cc", "src/cpp/server/server_builder.cc",
"src/cpp/server/server_callback.cc",
"src/cpp/server/server_cc.cc", "src/cpp/server/server_cc.cc",
"src/cpp/server/server_context.cc", "src/cpp/server/server_context.cc",
"src/cpp/server/server_credentials.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/security/auth_context.h",
"include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/serialization_traits.h",
"include/grpcpp/impl/codegen/server_callback.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_callback_impl.h",
"include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context.h",
"include/grpcpp/impl/codegen/server_context_impl.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/grpc++/test/server_context_test_spouse.h",
"include/grpcpp/test/mock_stream.h", "include/grpcpp/test/mock_stream.h",
"include/grpcpp/test/server_context_test_spouse.h", "include/grpcpp/test/server_context_test_spouse.h",
"include/grpcpp/test/default_reactor_test_peer.h",
], ],
deps = [ deps = [
":grpc++", ":grpc++",

@ -1120,6 +1120,7 @@ config("grpc_config") {
"include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/security/auth_context.h",
"include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/serialization_traits.h",
"include/grpcpp/impl/codegen/server_callback.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_callback_impl.h",
"include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context.h",
"include/grpcpp/impl/codegen/server_context_impl.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.cc",
"src/cpp/server/secure_server_credentials.h", "src/cpp/server/secure_server_credentials.h",
"src/cpp/server/server_builder.cc", "src/cpp/server/server_builder.cc",
"src/cpp/server/server_callback.cc",
"src/cpp/server/server_cc.cc", "src/cpp/server/server_cc.cc",
"src/cpp/server/server_context.cc", "src/cpp/server/server_context.cc",
"src/cpp/server/server_credentials.cc", "src/cpp/server/server_credentials.cc",

@ -3288,6 +3288,7 @@ add_library(grpc++
src/cpp/server/health/health_check_service.cc src/cpp/server/health/health_check_service.cc
src/cpp/server/health/health_check_service_server_builder_option.cc src/cpp/server/health/health_check_service_server_builder_option.cc
src/cpp/server/server_builder.cc src/cpp/server/server_builder.cc
src/cpp/server/server_callback.cc
src/cpp/server/server_cc.cc src/cpp/server/server_cc.cc
src/cpp/server/server_context.cc src/cpp/server/server_context.cc
src/cpp/server/server_credentials.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/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/serialization_traits.h
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h
include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context.h
include/grpcpp/impl/codegen/server_context_impl.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/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/serialization_traits.h
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h
include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context.h
include/grpcpp/impl/codegen/server_context_impl.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/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/serialization_traits.h
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h
include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context.h
include/grpcpp/impl/codegen/server_context_impl.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.cc
src/cpp/server/health/health_check_service_server_builder_option.cc src/cpp/server/health/health_check_service_server_builder_option.cc
src/cpp/server/server_builder.cc src/cpp/server/server_builder.cc
src/cpp/server/server_callback.cc
src/cpp/server/server_cc.cc src/cpp/server/server_cc.cc
src/cpp/server/server_context.cc src/cpp/server/server_context.cc
src/cpp/server/server_credentials.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/security/auth_context.h
include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/serialization_traits.h
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h
include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context.h
include/grpcpp/impl/codegen/server_context_impl.h include/grpcpp/impl/codegen/server_context_impl.h

@ -5685,6 +5685,7 @@ LIBGRPC++_SRC = \
src/cpp/server/health/health_check_service.cc \ src/cpp/server/health/health_check_service.cc \
src/cpp/server/health/health_check_service_server_builder_option.cc \ src/cpp/server/health/health_check_service_server_builder_option.cc \
src/cpp/server/server_builder.cc \ src/cpp/server/server_builder.cc \
src/cpp/server/server_callback.cc \
src/cpp/server/server_cc.cc \ src/cpp/server/server_cc.cc \
src/cpp/server/server_context.cc \ src/cpp/server/server_context.cc \
src/cpp/server/server_credentials.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/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/serialization_traits.h \
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h \
include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context.h \
include/grpcpp/impl/codegen/server_context_impl.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/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/serialization_traits.h \
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h \
include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context.h \
include/grpcpp/impl/codegen/server_context_impl.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/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/serialization_traits.h \
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h \
include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context.h \
include/grpcpp/impl/codegen/server_context_impl.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.cc \
src/cpp/server/health/health_check_service_server_builder_option.cc \ src/cpp/server/health/health_check_service_server_builder_option.cc \
src/cpp/server/server_builder.cc \ src/cpp/server/server_builder.cc \
src/cpp/server/server_callback.cc \
src/cpp/server/server_cc.cc \ src/cpp/server/server_cc.cc \
src/cpp/server/server_context.cc \ src/cpp/server/server_context.cc \
src/cpp/server/server_credentials.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/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/serialization_traits.h \
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h \
include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context.h \
include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_context_impl.h \

@ -397,6 +397,7 @@ filegroups:
- include/grpcpp/impl/codegen/security/auth_context.h - include/grpcpp/impl/codegen/security/auth_context.h
- include/grpcpp/impl/codegen/serialization_traits.h - include/grpcpp/impl/codegen/serialization_traits.h
- include/grpcpp/impl/codegen/server_callback.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_callback_impl.h
- include/grpcpp/impl/codegen/server_context.h - include/grpcpp/impl/codegen/server_context.h
- include/grpcpp/impl/codegen/server_context_impl.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.cc
- src/cpp/server/health/health_check_service_server_builder_option.cc - src/cpp/server/health/health_check_service_server_builder_option.cc
- src/cpp/server/server_builder.cc - src/cpp/server/server_builder.cc
- src/cpp/server/server_callback.cc
- src/cpp/server/server_cc.cc - src/cpp/server/server_cc.cc
- src/cpp/server/server_context.cc - src/cpp/server/server_context.cc
- src/cpp/server/server_credentials.cc - src/cpp/server/server_credentials.cc
@ -618,6 +620,7 @@ filegroups:
public_headers: public_headers:
- include/grpc++/test/mock_stream.h - include/grpc++/test/mock_stream.h
- include/grpc++/test/server_context_test_spouse.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/mock_stream.h
- include/grpcpp/test/server_context_test_spouse.h - include/grpcpp/test/server_context_test_spouse.h
deps: deps:

@ -136,6 +136,7 @@ Pod::Spec.new do |s|
'include/grpcpp/impl/codegen/security/auth_context.h', 'include/grpcpp/impl/codegen/security/auth_context.h',
'include/grpcpp/impl/codegen/serialization_traits.h', 'include/grpcpp/impl/codegen/serialization_traits.h',
'include/grpcpp/impl/codegen/server_callback.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_callback_impl.h',
'include/grpcpp/impl/codegen/server_context.h', 'include/grpcpp/impl/codegen/server_context.h',
'include/grpcpp/impl/codegen/server_context_impl.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.cc',
'src/cpp/server/secure_server_credentials.h', 'src/cpp/server/secure_server_credentials.h',
'src/cpp/server/server_builder.cc', 'src/cpp/server/server_builder.cc',
'src/cpp/server/server_callback.cc',
'src/cpp/server/server_cc.cc', 'src/cpp/server/server_cc.cc',
'src/cpp/server/server_context.cc', 'src/cpp/server/server_context.cc',
'src/cpp/server/server_credentials.cc', 'src/cpp/server/server_credentials.cc',

@ -1541,6 +1541,7 @@
'src/cpp/server/health/health_check_service.cc', 'src/cpp/server/health/health_check_service.cc',
'src/cpp/server/health/health_check_service_server_builder_option.cc', 'src/cpp/server/health/health_check_service_server_builder_option.cc',
'src/cpp/server/server_builder.cc', 'src/cpp/server/server_builder.cc',
'src/cpp/server/server_callback.cc',
'src/cpp/server/server_cc.cc', 'src/cpp/server/server_cc.cc',
'src/cpp/server/server_context.cc', 'src/cpp/server/server_context.cc',
'src/cpp/server/server_credentials.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.cc',
'src/cpp/server/health/health_check_service_server_builder_option.cc', 'src/cpp/server/health/health_check_service_server_builder_option.cc',
'src/cpp/server/server_builder.cc', 'src/cpp/server/server_builder.cc',
'src/cpp/server/server_callback.cc',
'src/cpp/server/server_cc.cc', 'src/cpp/server/server_cc.cc',
'src/cpp/server/server_context.cc', 'src/cpp/server/server_context.cc',
'src/cpp/server/server_credentials.cc', 'src/cpp/server/server_credentials.cc',

@ -726,6 +726,10 @@ typedef struct grpc_experimental_completion_queue_functor {
operation succeeded (non-zero) or failed (zero) */ operation succeeded (non-zero) or failed (zero) */
void (*functor_run)(struct grpc_experimental_completion_queue_functor*, int); 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. */ /** The following fields are not API. They are meant for internal use. */
int internal_success; int internal_success;
struct grpc_experimental_completion_queue_functor* internal_next; struct grpc_experimental_completion_queue_functor* internal_next;

@ -21,6 +21,7 @@
#include <grpcpp/impl/codegen/async_stream_impl.h> #include <grpcpp/impl/codegen/async_stream_impl.h>
#include <grpcpp/impl/codegen/byte_buffer.h> #include <grpcpp/impl/codegen/byte_buffer.h>
#include <grpcpp/impl/codegen/server_callback_handlers.h>
#include <grpcpp/impl/codegen/server_callback_impl.h> #include <grpcpp/impl/codegen/server_callback_impl.h>
struct grpc_server; struct grpc_server;
@ -42,12 +43,12 @@ class GenericServerContext final : public ::grpc_impl::ServerContext {
private: private:
friend class grpc_impl::Server; friend class grpc_impl::Server;
friend class ServerInterface; friend class grpc::ServerInterface;
void Clear() { void Clear() {
method_.clear(); method_.clear();
host_.clear(); host_.clear();
ServerContext::Clear(); ::grpc_impl::ServerContext::Clear();
} }
grpc::string method_; grpc::string method_;
@ -89,39 +90,30 @@ class AsyncGenericService final {
namespace experimental { namespace experimental {
/// \a ServerGenericBidiReactor is the reactor class for bidi streaming RPCs /// \a ServerGenericBidiReactor is the reactor class for bidi streaming RPCs
/// invoked on a CallbackGenericService. The API difference relative to /// invoked on a CallbackGenericService. It is just a ServerBidi reactor with
/// ServerBidiReactor is that the argument to OnStarted is a /// ByteBuffer arguments.
/// GenericServerContext rather than a ServerContext. All other reaction and using ServerGenericBidiReactor =
/// operation initiation APIs are the same as ServerBidiReactor. ::grpc_impl::experimental::ServerBidiReactor<ByteBuffer, ByteBuffer>;
class ServerGenericBidiReactor
: public ::grpc_impl::experimental::ServerBidiReactor<ByteBuffer, class GenericCallbackServerContext final
ByteBuffer> { : public ::grpc_impl::experimental::CallbackServerContext {
public: public:
/// Similar to ServerBidiReactor::OnStarted except for argument type. const grpc::string& method() const { return method_; }
/// const grpc::string& host() const { return host_; }
/// \param[in] context The context object associated with this RPC.
virtual void OnStarted(GenericServerContext* /*context*/) {}
private: private:
void OnStarted(::grpc_impl::ServerContext* ctx) final { friend class ::grpc_impl::Server;
OnStarted(static_cast<GenericServerContext*>(ctx)); friend class ::grpc::ServerInterface;
}
};
} // namespace experimental
namespace internal { void Clear() {
class UnimplementedGenericBidiReactor method_.clear();
: public experimental::ServerGenericBidiReactor { host_.clear();
public: ::grpc_impl::experimental::CallbackServerContext::Clear();
void OnDone() override { delete this; }
void OnStarted(GenericServerContext*) override {
this->Finish(Status(StatusCode::UNIMPLEMENTED, ""));
} }
};
} // namespace internal
namespace experimental { grpc::string method_;
grpc::string host_;
};
/// \a CallbackGenericService is the base class for generic services implemented /// \a CallbackGenericService is the base class for generic services implemented
/// using the callback API and registered through the ServerBuilder using /// using the callback API and registered through the ServerBuilder using
@ -132,10 +124,16 @@ class CallbackGenericService {
virtual ~CallbackGenericService() {} virtual ~CallbackGenericService() {}
/// The "method handler" for the generic API. This function should be /// The "method handler" for the generic API. This function should be
/// overridden to return a ServerGenericBidiReactor that implements the /// overridden to provide a ServerGenericBidiReactor that implements the
/// application-level interface for this RPC. /// application-level interface for this RPC. Unimplemented by default.
virtual ServerGenericBidiReactor* CreateReactor() { virtual ServerGenericBidiReactor* CreateReactor(
return new internal::UnimplementedGenericBidiReactor; GenericCallbackServerContext* /*ctx*/) {
class Reactor : public ServerGenericBidiReactor {
public:
Reactor() { this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); }
void OnDone() override { delete this; }
};
return new Reactor;
} }
private: private:
@ -145,7 +143,9 @@ class CallbackGenericService {
Handler() { Handler() {
return new ::grpc_impl::internal::CallbackBidiHandler<ByteBuffer, return new ::grpc_impl::internal::CallbackBidiHandler<ByteBuffer,
ByteBuffer>( ByteBuffer>(
[this] { return CreateReactor(); }); [this](::grpc_impl::experimental::CallbackServerContext* ctx) {
return CreateReactor(static_cast<GenericCallbackServerContext*>(ctx));
});
} }
grpc_impl::Server* server_{nullptr}; grpc_impl::Server* server_{nullptr};

@ -47,8 +47,8 @@ void CatchingCallback(Func&& func, Args&&... args) {
#endif // GRPC_ALLOW_EXCEPTIONS #endif // GRPC_ALLOW_EXCEPTIONS
} }
template <class ReturnType, class Func, class... Args> template <class Reactor, class Func, class... Args>
ReturnType* CatchingReactorCreator(Func&& func, Args&&... args) { Reactor* CatchingReactorGetter(Func&& func, Args&&... args) {
#if GRPC_ALLOW_EXCEPTIONS #if GRPC_ALLOW_EXCEPTIONS
try { try {
return func(std::forward<Args>(args)...); return func(std::forward<Args>(args)...);
@ -85,6 +85,10 @@ class CallbackWithStatusTag
: call_(call), func_(std::move(f)), ops_(ops) { : call_(call), func_(std::move(f)), ops_(ops) {
g_core_codegen_interface->grpc_call_ref(call); g_core_codegen_interface->grpc_call_ref(call);
functor_run = &CallbackWithStatusTag::StaticRun; 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() {} ~CallbackWithStatusTag() {}
Status* status_ptr() { return &status_; } Status* status_ptr() { return &status_; }
@ -147,8 +151,8 @@ class CallbackWithSuccessTag
CallbackWithSuccessTag() : call_(nullptr) {} CallbackWithSuccessTag() : call_(nullptr) {}
CallbackWithSuccessTag(grpc_call* call, std::function<void(bool)> f, CallbackWithSuccessTag(grpc_call* call, std::function<void(bool)> f,
CompletionQueueTag* ops) { CompletionQueueTag* ops, bool can_inline) {
Set(call, f, ops); Set(call, f, ops, can_inline);
} }
CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete; CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete;
@ -159,14 +163,18 @@ class CallbackWithSuccessTag
// Set can only be called on a default-constructed or Clear'ed tag. // 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 // 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. // 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<void(bool)> f, void Set(grpc_call* call, std::function<void(bool)> f,
CompletionQueueTag* ops) { CompletionQueueTag* ops, bool can_inline) {
GPR_CODEGEN_ASSERT(call_ == nullptr); GPR_CODEGEN_ASSERT(call_ == nullptr);
g_core_codegen_interface->grpc_call_ref(call); g_core_codegen_interface->grpc_call_ref(call);
call_ = call; call_ = call;
func_ = std::move(f); func_ = std::move(f);
ops_ = ops; ops_ = ops;
functor_run = &CallbackWithSuccessTag::StaticRun; functor_run = &CallbackWithSuccessTag::StaticRun;
inlineable = can_inline;
} }
void Clear() { void Clear() {

@ -457,7 +457,7 @@ class ClientCallbackReaderWriterImpl
reactor_->OnReadInitialMetadataDone(ok); reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&start_ops_); &start_ops_, /*can_inline=*/false);
if (!start_corked_) { if (!start_corked_) {
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags()); context_->initial_metadata_flags());
@ -473,7 +473,7 @@ class ClientCallbackReaderWriterImpl
reactor_->OnWriteDone(ok); reactor_->OnWriteDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&write_ops_); &write_ops_, /*can_inline=*/false);
write_ops_.set_core_cq_tag(&write_tag_); write_ops_.set_core_cq_tag(&write_tag_);
read_tag_.Set(call_.call(), read_tag_.Set(call_.call(),
@ -481,7 +481,7 @@ class ClientCallbackReaderWriterImpl
reactor_->OnReadDone(ok); reactor_->OnReadDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&read_ops_); &read_ops_, /*can_inline=*/false);
read_ops_.set_core_cq_tag(&read_tag_); read_ops_.set_core_cq_tag(&read_tag_);
if (read_ops_at_start_) { if (read_ops_at_start_) {
call_.PerformOps(&read_ops_); call_.PerformOps(&read_ops_);
@ -496,7 +496,7 @@ class ClientCallbackReaderWriterImpl
} }
finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); },
&finish_ops_); &finish_ops_, /*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_); finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_); call_.PerformOps(&finish_ops_);
@ -544,7 +544,7 @@ class ClientCallbackReaderWriterImpl
reactor_->OnWritesDoneDone(ok); reactor_->OnWritesDoneDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&writes_done_ops_); &writes_done_ops_, /*can_inline=*/false);
writes_done_ops_.set_core_cq_tag(&writes_done_tag_); writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (started_) { if (started_) {
@ -668,7 +668,7 @@ class ClientCallbackReaderImpl
reactor_->OnReadInitialMetadataDone(ok); reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&start_ops_); &start_ops_, /*can_inline=*/false);
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags()); context_->initial_metadata_flags());
start_ops_.RecvInitialMetadata(context_); start_ops_.RecvInitialMetadata(context_);
@ -681,14 +681,14 @@ class ClientCallbackReaderImpl
reactor_->OnReadDone(ok); reactor_->OnReadDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&read_ops_); &read_ops_, /*can_inline=*/false);
read_ops_.set_core_cq_tag(&read_tag_); read_ops_.set_core_cq_tag(&read_tag_);
if (read_ops_at_start_) { if (read_ops_at_start_) {
call_.PerformOps(&read_ops_); call_.PerformOps(&read_ops_);
} }
finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); },
&finish_ops_); &finish_ops_, /*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_); finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_); call_.PerformOps(&finish_ops_);
@ -808,7 +808,7 @@ class ClientCallbackWriterImpl
reactor_->OnReadInitialMetadataDone(ok); reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&start_ops_); &start_ops_, /*can_inline=*/false);
if (!start_corked_) { if (!start_corked_) {
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags()); context_->initial_metadata_flags());
@ -824,7 +824,7 @@ class ClientCallbackWriterImpl
reactor_->OnWriteDone(ok); reactor_->OnWriteDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&write_ops_); &write_ops_, /*can_inline=*/false);
write_ops_.set_core_cq_tag(&write_tag_); write_ops_.set_core_cq_tag(&write_tag_);
if (write_ops_at_start_) { if (write_ops_at_start_) {
@ -836,7 +836,7 @@ class ClientCallbackWriterImpl
} }
finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); },
&finish_ops_); &finish_ops_, /*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_); finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_); call_.PerformOps(&finish_ops_);
@ -874,7 +874,7 @@ class ClientCallbackWriterImpl
reactor_->OnWritesDoneDone(ok); reactor_->OnWritesDoneDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&writes_done_ops_); &writes_done_ops_, /*can_inline=*/false);
writes_done_ops_.set_core_cq_tag(&writes_done_tag_); writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (started_) { if (started_) {
@ -983,7 +983,7 @@ class ClientCallbackUnaryImpl final : public experimental::ClientCallbackUnary {
reactor_->OnReadInitialMetadataDone(ok); reactor_->OnReadInitialMetadataDone(ok);
MaybeFinish(); MaybeFinish();
}, },
&start_ops_); &start_ops_, /*can_inline=*/false);
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags()); context_->initial_metadata_flags());
start_ops_.RecvInitialMetadata(context_); start_ops_.RecvInitialMetadata(context_);
@ -991,7 +991,7 @@ class ClientCallbackUnaryImpl final : public experimental::ClientCallbackUnary {
call_.PerformOps(&start_ops_); call_.PerformOps(&start_ops_);
finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, finish_tag_.Set(call_.call(), [this](bool /*ok*/) { MaybeFinish(); },
&finish_ops_); &finish_ops_, /*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_); finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_); finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_); call_.PerformOps(&finish_ops_);

@ -66,6 +66,7 @@ template <class InputMessage, class OutputMessage>
class BlockingUnaryCallImpl; class BlockingUnaryCallImpl;
class CallOpClientRecvStatus; class CallOpClientRecvStatus;
class CallOpRecvInitialMetadata; class CallOpRecvInitialMetadata;
class ServerContextImpl;
} // namespace internal } // namespace internal
namespace testing { namespace testing {
@ -106,6 +107,11 @@ class ClientAsyncReaderWriter;
template <class R> template <class R>
class ClientAsyncResponseReader; class ClientAsyncResponseReader;
namespace experimental {
class ServerContextBase;
class CallbackServerContext;
} // namespace experimental
/// Options for \a ClientContext::FromServerContext specifying which traits from /// Options for \a ClientContext::FromServerContext specifying which traits from
/// the \a ServerContext to propagate (copy) from it into a new \a /// the \a ServerContext to propagate (copy) from it into a new \a
/// ClientContext. /// ClientContext.
@ -195,6 +201,9 @@ class ClientContext {
static std::unique_ptr<ClientContext> FromServerContext( static std::unique_ptr<ClientContext> FromServerContext(
const grpc_impl::ServerContext& server_context, const grpc_impl::ServerContext& server_context,
PropagationOptions options = PropagationOptions()); PropagationOptions options = PropagationOptions());
static std::unique_ptr<ClientContext> 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 /// 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 /// a client call. These are made available at the server side by the \a
@ -474,6 +483,10 @@ class ClientContext {
void SendCancelToInterceptors(); void SendCancelToInterceptors();
static std::unique_ptr<ClientContext> FromInternalServerContext(
const grpc_impl::experimental::ServerContextBase& server_context,
PropagationOptions options);
bool initial_metadata_received_; bool initial_metadata_received_;
bool wait_for_ready_; bool wait_for_ready_;
bool wait_for_ready_explicitly_set_; bool wait_for_ready_explicitly_set_;

@ -46,7 +46,6 @@ namespace grpc_impl {
class Channel; class Channel;
class Server; class Server;
class ServerBuilder; class ServerBuilder;
class ServerContext;
template <class R> template <class R>
class ClientReader; class ClientReader;
template <class W> template <class W>
@ -57,6 +56,9 @@ template <class R>
class ServerReader; class ServerReader;
template <class W> template <class W>
class ServerWriter; class ServerWriter;
namespace experimental {
class ServerContextBase;
} // namespace experimental
namespace internal { namespace internal {
template <class W, class R> template <class W, class R>
class ServerReaderWriterBody; class ServerReaderWriterBody;
@ -275,7 +277,7 @@ class CompletionQueue : private ::grpc::GrpcLibraryCodegen {
template <::grpc::StatusCode code> template <::grpc::StatusCode code>
friend class ::grpc_impl::internal::ErrorMethodHandler; friend class ::grpc_impl::internal::ErrorMethodHandler;
friend class ::grpc_impl::Server; friend class ::grpc_impl::Server;
friend class ::grpc_impl::ServerContext; friend class ::grpc_impl::experimental::ServerContextBase;
friend class ::grpc::ServerInterface; friend class ::grpc::ServerInterface;
template <class InputMessage, class OutputMessage> template <class InputMessage, class OutputMessage>
friend class ::grpc::internal::BlockingUnaryCallImpl; friend class ::grpc::internal::BlockingUnaryCallImpl;

@ -65,8 +65,10 @@ class RpcMethodHandler : public ::grpc::internal::MethodHandler {
::grpc::Status status = param.status; ::grpc::Status status = param.status;
if (status.ok()) { if (status.ok()) {
status = CatchingFunctionHandler([this, &param, &rsp] { status = CatchingFunctionHandler([this, &param, &rsp] {
return func_(service_, param.server_context, return func_(
static_cast<RequestType*>(param.request), &rsp); service_,
static_cast<::grpc_impl::ServerContext*>(param.server_context),
static_cast<RequestType*>(param.request), &rsp);
}); });
static_cast<RequestType*>(param.request)->~RequestType(); static_cast<RequestType*>(param.request)->~RequestType();
} }
@ -128,12 +130,16 @@ class ClientStreamingHandler : public ::grpc::internal::MethodHandler {
: func_(func), service_(service) {} : func_(func), service_(service) {}
void RunHandler(const HandlerParameter& param) final { void RunHandler(const HandlerParameter& param) final {
::grpc_impl::ServerReader<RequestType> reader(param.call, ::grpc_impl::ServerReader<RequestType> reader(
param.server_context); param.call,
static_cast<::grpc_impl::ServerContext*>(param.server_context));
ResponseType rsp; ResponseType rsp;
::grpc::Status status = ::grpc::Status status =
CatchingFunctionHandler([this, &param, &reader, &rsp] { CatchingFunctionHandler([this, &param, &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, ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
@ -178,11 +184,14 @@ class ServerStreamingHandler : public ::grpc::internal::MethodHandler {
void RunHandler(const HandlerParameter& param) final { void RunHandler(const HandlerParameter& param) final {
::grpc::Status status = param.status; ::grpc::Status status = param.status;
if (status.ok()) { if (status.ok()) {
::grpc_impl::ServerWriter<ResponseType> writer(param.call, ::grpc_impl::ServerWriter<ResponseType> writer(
param.server_context); param.call,
static_cast<::grpc_impl::ServerContext*>(param.server_context));
status = CatchingFunctionHandler([this, &param, &writer] { status = CatchingFunctionHandler([this, &param, &writer] {
return func_(service_, param.server_context, return func_(
static_cast<RequestType*>(param.request), &writer); service_,
static_cast<::grpc_impl::ServerContext*>(param.server_context),
static_cast<RequestType*>(param.request), &writer);
}); });
static_cast<RequestType*>(param.request)->~RequestType(); static_cast<RequestType*>(param.request)->~RequestType();
} }
@ -246,9 +255,12 @@ class TemplatedBidiStreamingHandler : public ::grpc::internal::MethodHandler {
: func_(func), write_needed_(WriteNeeded) {} : func_(func), write_needed_(WriteNeeded) {}
void RunHandler(const HandlerParameter& param) final { 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, &param, &stream] { ::grpc::Status status = CatchingFunctionHandler([this, &param, &stream] {
return func_(param.server_context, &stream); return func_(
static_cast<::grpc_impl::ServerContext*>(param.server_context),
&stream);
}); });
::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata,
@ -333,7 +345,8 @@ template <::grpc::StatusCode code>
class ErrorMethodHandler : public ::grpc::internal::MethodHandler { class ErrorMethodHandler : public ::grpc::internal::MethodHandler {
public: public:
template <class T> template <class T>
static void FillOps(::grpc_impl::ServerContext* context, T* ops) { static void FillOps(::grpc_impl::experimental::ServerContextBase* context,
T* ops) {
::grpc::Status status(code, ""); ::grpc::Status status(code, "");
if (!context->sent_initial_metadata_) { if (!context->sent_initial_metadata_) {
ops->SendInitialMetadata(&context->initial_metadata_, ops->SendInitialMetadata(&context->initial_metadata_,

@ -32,8 +32,10 @@
#include <grpcpp/impl/codegen/status.h> #include <grpcpp/impl/codegen/status.h>
namespace grpc_impl { namespace grpc_impl {
class ServerContext; namespace experimental {
class ServerContextBase;
} }
} // namespace grpc_impl
namespace grpc { namespace grpc {
namespace internal { namespace internal {
@ -52,8 +54,9 @@ class MethodHandler {
/// \param requester : used only by the callback API. It is a function /// \param requester : used only by the callback API. It is a function
/// called by the RPC Controller to request another RPC (and also /// called by the RPC Controller to request another RPC (and also
/// to set up the state required to make that request possible) /// to set up the state required to make that request possible)
HandlerParameter(Call* c, ::grpc_impl::ServerContext* context, void* req, HandlerParameter(Call* c,
Status req_status, void* handler_data, ::grpc_impl::experimental::ServerContextBase* context,
void* req, Status req_status, void* handler_data,
std::function<void()> requester) std::function<void()> requester)
: call(c), : call(c),
server_context(context), server_context(context),
@ -63,7 +66,7 @@ class MethodHandler {
call_requester(std::move(requester)) {} call_requester(std::move(requester)) {}
~HandlerParameter() {} ~HandlerParameter() {}
Call* const call; Call* const call;
::grpc_impl::ServerContext* const server_context; ::grpc_impl::experimental::ServerContextBase* const server_context;
void* const request; void* const request;
const Status status; const Status status;
void* const internal_data; void* const internal_data;

@ -23,20 +23,18 @@
namespace grpc { namespace grpc {
namespace experimental { namespace experimental {
template <class Request, class Response> template <class Request>
using ServerReadReactor = using ServerReadReactor = ::grpc_impl::experimental::ServerReadReactor<Request>;
::grpc_impl::experimental::ServerReadReactor<Request, Response>;
template <class Request, class Response> template <class Response>
using ServerWriteReactor = using ServerWriteReactor =
::grpc_impl::experimental::ServerWriteReactor<Request, Response>; ::grpc_impl::experimental::ServerWriteReactor<Response>;
template <class Request, class Response> template <class Request, class Response>
using ServerBidiReactor = using ServerBidiReactor =
::grpc_impl::experimental::ServerBidiReactor<Request, Response>; ::grpc_impl::experimental::ServerBidiReactor<Request, Response>;
typedef ::grpc_impl::experimental::ServerCallbackRpcController using ServerUnaryReactor = ::grpc_impl::experimental::ServerUnaryReactor;
ServerCallbackRpcController;
} // namespace experimental } // namespace experimental
} // namespace grpc } // namespace grpc

@ -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 <grpcpp/impl/codegen/message_allocator.h>
#include <grpcpp/impl/codegen/rpc_service_method.h>
#include <grpcpp/impl/codegen/server_callback_impl.h>
#include <grpcpp/impl/codegen/server_context_impl.h>
#include <grpcpp/impl/codegen/status.h>
namespace grpc_impl {
namespace internal {
template <class RequestType, class ResponseType>
class CallbackUnaryHandler : public ::grpc::internal::MethodHandler {
public:
explicit CallbackUnaryHandler(
std::function<experimental::ServerUnaryReactor*(
::grpc_impl::experimental::CallbackServerContext*, const RequestType*,
ResponseType*)>
get_reactor)
: get_reactor_(std::move(get_reactor)) {}
void SetMessageAllocator(
::grpc::experimental::MessageAllocator<RequestType, ResponseType>*
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<RequestType, ResponseType>*>(
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<RequestType, ResponseType>*
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<RequestType, ResponseType>)))
DefaultMessageHolder<RequestType, ResponseType>();
}
*handler_data = allocator_state;
request = allocator_state->request();
*status =
::grpc::SerializationTraits<RequestType>::Deserialize(&buf, request);
buf.Release();
if (status->ok()) {
return request;
}
// Clean up on deserialization failure.
allocator_state->Release();
return nullptr;
}
private:
std::function<experimental::ServerUnaryReactor*(
::grpc_impl::experimental::CallbackServerContext*, const RequestType*,
ResponseType*)>
get_reactor_;
::grpc::experimental::MessageAllocator<RequestType, ResponseType>*
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<RequestType, ResponseType>;
ServerCallbackUnaryImpl(
::grpc_impl::experimental::CallbackServerContext* ctx,
::grpc::internal::Call* call,
::grpc::experimental::MessageHolder<RequestType, ResponseType>*
allocator_state,
std::function<void()> 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<RequestType, ResponseType>* const
allocator_state_;
std::function<void()> 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<experimental::ServerUnaryReactor*> reactor_;
// callbacks_outstanding_ follows a refcount pattern
std::atomic<intptr_t> callbacks_outstanding_{
3}; // reserve for start, Finish, and CompletionOp
};
};
template <class RequestType, class ResponseType>
class CallbackClientStreamingHandler : public ::grpc::internal::MethodHandler {
public:
explicit CallbackClientStreamingHandler(
std::function<experimental::ServerReadReactor<RequestType>*(
::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<RequestType>* reactor = nullptr;
if (param.status.ok()) {
reactor = ::grpc::internal::CatchingReactorGetter<
experimental::ServerReadReactor<RequestType>>(
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<RequestType>)))
UnimplementedReadReactor<RequestType>(
::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""));
}
reader->SetupReactor(reactor);
}
private:
std::function<experimental::ServerReadReactor<RequestType>*(
::grpc_impl::experimental::CallbackServerContext*, ResponseType*)>
get_reactor_;
class ServerCallbackReaderImpl
: public experimental::ServerCallbackReader<RequestType> {
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<RequestType, ResponseType>;
ServerCallbackReaderImpl(
::grpc_impl::experimental::CallbackServerContext* ctx,
::grpc::internal::Call* call, std::function<void()> call_requester)
: ctx_(ctx), call_(*call), call_requester_(std::move(call_requester)) {}
void SetupReactor(experimental::ServerReadReactor<RequestType>* 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<RequestType>>
read_ops_;
::grpc::internal::CallbackWithSuccessTag read_tag_;
::grpc_impl::experimental::CallbackServerContext* const ctx_;
::grpc::internal::Call call_;
ResponseType resp_;
std::function<void()> call_requester_;
// The memory ordering of reactor_ follows ServerCallbackUnaryImpl.
std::atomic<experimental::ServerReadReactor<RequestType>*> reactor_;
// callbacks_outstanding_ follows a refcount pattern
std::atomic<intptr_t> callbacks_outstanding_{
3}; // reserve for OnStarted, Finish, and CompletionOp
};
};
template <class RequestType, class ResponseType>
class CallbackServerStreamingHandler : public ::grpc::internal::MethodHandler {
public:
explicit CallbackServerStreamingHandler(
std::function<experimental::ServerWriteReactor<ResponseType>*(
::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<RequestType*>(param.request),
std::move(param.call_requester));
param.server_context->BeginCompletionOp(
param.call, [writer](bool) { writer->MaybeDone(); }, writer);
experimental::ServerWriteReactor<ResponseType>* reactor = nullptr;
if (param.status.ok()) {
reactor = ::grpc::internal::CatchingReactorGetter<
experimental::ServerWriteReactor<ResponseType>>(
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<ResponseType>)))
UnimplementedWriteReactor<ResponseType>(
::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<RequestType>::Deserialize(&buf, request);
buf.Release();
if (status->ok()) {
return request;
}
request->~RequestType();
return nullptr;
}
private:
std::function<experimental::ServerWriteReactor<ResponseType>*(
::grpc_impl::experimental::CallbackServerContext*, const RequestType*)>
get_reactor_;
class ServerCallbackWriterImpl
: public experimental::ServerCallbackWriter<ResponseType> {
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<RequestType, ResponseType>;
ServerCallbackWriterImpl(
::grpc_impl::experimental::CallbackServerContext* ctx,
::grpc::internal::Call* call, const RequestType* req,
std::function<void()> call_requester)
: ctx_(ctx),
call_(*call),
req_(req),
call_requester_(std::move(call_requester)) {}
void SetupReactor(experimental::ServerWriteReactor<ResponseType>* 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<void()> call_requester_;
// The memory ordering of reactor_ follows ServerCallbackUnaryImpl.
std::atomic<experimental::ServerWriteReactor<ResponseType>*> reactor_;
// callbacks_outstanding_ follows a refcount pattern
std::atomic<intptr_t> callbacks_outstanding_{
3}; // reserve for OnStarted, Finish, and CompletionOp
};
};
template <class RequestType, class ResponseType>
class CallbackBidiHandler : public ::grpc::internal::MethodHandler {
public:
explicit CallbackBidiHandler(
std::function<experimental::ServerBidiReactor<RequestType, ResponseType>*(
::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<RequestType, ResponseType>* reactor =
nullptr;
if (param.status.ok()) {
reactor = ::grpc::internal::CatchingReactorGetter<
experimental::ServerBidiReactor<RequestType, ResponseType>>(
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<RequestType, ResponseType>)))
UnimplementedBidiReactor<RequestType, ResponseType>(
::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""));
}
stream->SetupReactor(reactor);
}
private:
std::function<experimental::ServerBidiReactor<RequestType, ResponseType>*(
::grpc_impl::experimental::CallbackServerContext*)>
get_reactor_;
class ServerCallbackReaderWriterImpl
: public experimental::ServerCallbackReaderWriter<RequestType,
ResponseType> {
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<RequestType, ResponseType>;
ServerCallbackReaderWriterImpl(
::grpc_impl::experimental::CallbackServerContext* ctx,
::grpc::internal::Call* call, std::function<void()> call_requester)
: ctx_(ctx), call_(*call), call_requester_(std::move(call_requester)) {}
void SetupReactor(
experimental::ServerBidiReactor<RequestType, ResponseType>* 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<RequestType>>
read_ops_;
::grpc::internal::CallbackWithSuccessTag read_tag_;
::grpc_impl::experimental::CallbackServerContext* const ctx_;
::grpc::internal::Call call_;
std::function<void()> call_requester_;
// The memory ordering of reactor_ follows ServerCallbackUnaryImpl.
std::atomic<experimental::ServerBidiReactor<RequestType, ResponseType>*>
reactor_;
// callbacks_outstanding_ follows a refcount pattern
std::atomic<intptr_t> callbacks_outstanding_{
3}; // reserve for OnStarted, Finish, and CompletionOp
};
};
} // namespace internal
} // namespace grpc_impl
#endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_HANDLERS_H

File diff suppressed because it is too large Load Diff

@ -22,7 +22,15 @@
#include <grpcpp/impl/codegen/server_context_impl.h> #include <grpcpp/impl/codegen/server_context_impl.h>
namespace grpc { namespace grpc {
typedef ::grpc_impl::ServerContext ServerContext; typedef ::grpc_impl::ServerContext ServerContext;
namespace experimental {
typedef ::grpc_impl::experimental::ServerContextBase ServerContextBase;
typedef ::grpc_impl::experimental::CallbackServerContext CallbackServerContext;
} // namespace experimental
} // namespace grpc } // namespace grpc
#endif // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H #endif // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H

@ -18,6 +18,8 @@
#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H #ifndef GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H
#define GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H #define GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H
#include <atomic>
#include <map> #include <map>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -30,9 +32,12 @@
#include <grpcpp/impl/codegen/completion_queue_tag.h> #include <grpcpp/impl/codegen/completion_queue_tag.h>
#include <grpcpp/impl/codegen/config.h> #include <grpcpp/impl/codegen/config.h>
#include <grpcpp/impl/codegen/create_auth_context.h> #include <grpcpp/impl/codegen/create_auth_context.h>
#include <grpcpp/impl/codegen/message_allocator.h>
#include <grpcpp/impl/codegen/metadata_map.h> #include <grpcpp/impl/codegen/metadata_map.h>
#include <grpcpp/impl/codegen/security/auth_context.h> #include <grpcpp/impl/codegen/security/auth_context.h>
#include <grpcpp/impl/codegen/server_callback_impl.h>
#include <grpcpp/impl/codegen/server_interceptor.h> #include <grpcpp/impl/codegen/server_interceptor.h>
#include <grpcpp/impl/codegen/status.h>
#include <grpcpp/impl/codegen/string_ref.h> #include <grpcpp/impl/codegen/string_ref.h>
#include <grpcpp/impl/codegen/time.h> #include <grpcpp/impl/codegen/time.h>
@ -72,6 +77,8 @@ template <class ServiceType, class RequestType, class ResponseType>
class ClientStreamingHandler; class ClientStreamingHandler;
template <class ServiceType, class RequestType, class ResponseType> template <class ServiceType, class RequestType, class ResponseType>
class RpcMethodHandler; class RpcMethodHandler;
template <class Base>
class FinishOnlyReactor;
template <class W, class R> template <class W, class R>
class ServerReaderWriterBody; class ServerReaderWriterBody;
template <class ServiceType, class RequestType, class ResponseType> template <class ServiceType, class RequestType, class ResponseType>
@ -88,6 +95,10 @@ namespace grpc {
class GenericServerContext; class GenericServerContext;
class ServerInterface; class ServerInterface;
namespace experimental {
class GenericCallbackServerContext;
} // namespace experimental
namespace internal { namespace internal {
class Call; class Call;
} // namespace internal } // namespace internal
@ -95,29 +106,18 @@ class Call;
namespace testing { namespace testing {
class InteropServerContextInspector; class InteropServerContextInspector;
class ServerContextTestSpouse; class ServerContextTestSpouse;
class DefaultReactorTestPeer;
} // namespace testing } // namespace testing
} // namespace grpc } // namespace grpc
namespace grpc_impl { namespace grpc_impl {
/// A ServerContext allows the person implementing a service handler to: namespace experimental {
///
/// - Add custom initial and trailing metadata key-value pairs that will /// Base class of ServerContext. Experimental until callback API is final.
/// propagated to the client side. class ServerContextBase {
/// - 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: public:
ServerContext(); // for async calls virtual ~ServerContextBase();
~ServerContext();
/// Return the deadline for the server call. /// Return the deadline for the server call.
std::chrono::system_clock::time_point deadline() const { std::chrono::system_clock::time_point deadline() const {
@ -258,6 +258,11 @@ class ServerContext {
/// Get the census context associated with this server call. /// Get the census context associated with this server call.
const struct census_context* census_context() const; 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. /// Async only. Has to be called before the rpc starts.
/// Returns the tag in completion queue when the rpc finishes. /// Returns the tag in completion queue when the rpc finishes.
/// IsCancelled() can then be called to check whether the rpc was cancelled. /// IsCancelled() can then be called to check whether the rpc was cancelled.
@ -268,13 +273,44 @@ class ServerContext {
async_notify_when_done_tag_ = tag; async_notify_when_done_tag_ = tag;
} }
/// Should be used for framework-level extensions only. /// NOTE: This is an API for advanced users who need custom allocators.
/// Applications never need to call this method. /// Get and maybe mutate the allocator state associated with the current RPC.
grpc_call* c_call() { return call_; } /// 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: private:
friend class ::grpc::testing::InteropServerContextInspector; friend class ::grpc::testing::InteropServerContextInspector;
friend class ::grpc::testing::ServerContextTestSpouse; friend class ::grpc::testing::ServerContextTestSpouse;
friend class ::grpc::testing::DefaultReactorTestPeer;
friend class ::grpc::ServerInterface; friend class ::grpc::ServerInterface;
friend class ::grpc_impl::Server; friend class ::grpc_impl::Server;
template <class W, class R> template <class W, class R>
@ -309,23 +345,24 @@ class ServerContext {
friend class ::grpc_impl::internal::CallbackBidiHandler; friend class ::grpc_impl::internal::CallbackBidiHandler;
template <::grpc::StatusCode code> template <::grpc::StatusCode code>
friend class ::grpc_impl::internal::ErrorMethodHandler; friend class ::grpc_impl::internal::ErrorMethodHandler;
template <class Base>
friend class ::grpc_impl::internal::FinishOnlyReactor;
friend class ::grpc_impl::ClientContext; friend class ::grpc_impl::ClientContext;
friend class ::grpc::GenericServerContext; friend class ::grpc::GenericServerContext;
friend class ::grpc::experimental::GenericCallbackServerContext;
/// Prevent copying. /// Prevent copying.
ServerContext(const ServerContext&); ServerContextBase(const ServerContextBase&);
ServerContext& operator=(const ServerContext&); ServerContextBase& operator=(const ServerContextBase&);
class CompletionOp; class CompletionOp;
void BeginCompletionOp(::grpc::internal::Call* call, void BeginCompletionOp(
std::function<void(bool)> callback, ::grpc::internal::Call* call, std::function<void(bool)> callback,
::grpc_impl::internal::ServerReactor* reactor); ::grpc_impl::internal::ServerCallbackCall* callback_controller);
/// Return the tag queued by BeginCompletionOp() /// Return the tag queued by BeginCompletionOp()
::grpc::internal::CompletionQueueTag* GetCompletionOpTag(); ::grpc::internal::CompletionQueueTag* GetCompletionOpTag();
ServerContext(gpr_timespec deadline, grpc_metadata_array* arr);
void set_call(grpc_call* call) { call_ = call; } void set_call(grpc_call* call) { call_ = call; }
void BindDeadlineAndMetadata(gpr_timespec deadline, grpc_metadata_array* arr); void BindDeadlineAndMetadata(gpr_timespec deadline, grpc_metadata_array* arr);
@ -336,9 +373,6 @@ class ServerContext {
uint32_t initial_metadata_flags() const { return 0; } uint32_t initial_metadata_flags() const { return 0; }
void SetCancelCallback(std::function<void()> callback);
void ClearCancelCallback();
::grpc::experimental::ServerRpcInfo* set_server_rpc_info( ::grpc::experimental::ServerRpcInfo* set_server_rpc_info(
const char* method, ::grpc::internal::RpcMethod::RpcType type, const char* method, ::grpc::internal::RpcMethod::RpcType type,
const std::vector<std::unique_ptr< const std::vector<std::unique_ptr<
@ -350,6 +384,11 @@ class ServerContext {
return rpc_info_; return rpc_info_;
} }
void set_message_allocator_state(
::grpc::experimental::RpcAllocatorState* allocator_state) {
message_allocator_state_ = allocator_state;
}
CompletionOp* completion_op_; CompletionOp* completion_op_;
bool has_notify_when_done_tag_; bool has_notify_when_done_tag_;
void* async_notify_when_done_tag_; void* async_notify_when_done_tag_;
@ -374,6 +413,176 @@ class ServerContext {
bool has_pending_ops_; bool has_pending_ops_;
::grpc::experimental::ServerRpcInfo* rpc_info_; ::grpc::experimental::ServerRpcInfo* rpc_info_;
::grpc::experimental::RpcAllocatorState* message_allocator_state_ = nullptr;
class Reactor : public experimental::ServerUnaryReactor {
public:
void OnCancel() override {}
void OnDone() override {}
// Override InternalInlineable for this class since its reactions are
// trivial and thus do not need to be run from the executor (triggering a
// thread hop). This should only be used by internal reactors (thus the
// name) and not by user application code.
bool InternalInlineable() override { return true; }
};
void SetupTestDefaultReactor(std::function<void(::grpc::Status)> 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<void(::grpc::Status)> 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<void(::grpc::Status s)> func_;
};
Reactor default_reactor_;
std::atomic_bool default_reactor_used_{false};
std::unique_ptr<TestServerCallbackUnary> 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 } // 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 #endif // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_IMPL_H

@ -27,8 +27,10 @@
#include <grpcpp/impl/codegen/string_ref.h> #include <grpcpp/impl/codegen/string_ref.h>
namespace grpc_impl { namespace grpc_impl {
class ServerContext; namespace experimental {
class ServerContextBase;
} }
} // namespace grpc_impl
namespace grpc { namespace grpc {
@ -80,7 +82,7 @@ class ServerRpcInfo {
/// Return a pointer to the underlying ServerContext structure associated /// Return a pointer to the underlying ServerContext structure associated
/// with the RPC to support features that apply to it /// 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: private:
static_assert(Type::UNARY == static_assert(Type::UNARY ==
@ -96,8 +98,8 @@ class ServerRpcInfo {
static_cast<Type>(internal::RpcMethod::BIDI_STREAMING), static_cast<Type>(internal::RpcMethod::BIDI_STREAMING),
"violated expectation about Type enum"); "violated expectation about Type enum");
ServerRpcInfo(grpc_impl::ServerContext* ctx, const char* method, ServerRpcInfo(grpc_impl::experimental::ServerContextBase* ctx,
internal::RpcMethod::RpcType type) const char* method, internal::RpcMethod::RpcType type)
: ctx_(ctx), method_(method), type_(static_cast<Type>(type)) {} : ctx_(ctx), method_(method), type_(static_cast<Type>(type)) {}
// Runs interceptor at pos \a pos. // 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 char* method_ = nullptr;
const Type type_; const Type type_;
std::atomic<intptr_t> ref_{1}; std::atomic<intptr_t> ref_{1};
std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_; std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
friend class internal::InterceptorBatchMethodsImpl; friend class internal::InterceptorBatchMethodsImpl;
friend class grpc_impl::ServerContext; friend class grpc_impl::experimental::ServerContextBase;
}; };
} // namespace experimental } // namespace experimental

@ -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 <grpcpp/server_context.h>
#include <grpcpp/support/server_callback.h>
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<void(::grpc::Status)> 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

@ -148,6 +148,7 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file,
"grpcpp/impl/codegen/proto_utils.h", "grpcpp/impl/codegen/proto_utils.h",
"grpcpp/impl/codegen/rpc_method.h", "grpcpp/impl/codegen/rpc_method.h",
"grpcpp/impl/codegen/server_callback.h", "grpcpp/impl/codegen/server_callback.h",
"grpcpp/impl/codegen/server_callback_handlers.h",
"grpcpp/impl/codegen/server_context.h", "grpcpp/impl/codegen/server_context.h",
"grpcpp/impl/codegen/service_type.h", "grpcpp/impl/codegen/service_type.h",
"grpcpp/impl/codegen/status.h", "grpcpp/impl/codegen/status.h",
@ -922,14 +923,12 @@ void PrintHeaderServerCallbackMethodsHelper(
" abort();\n" " abort();\n"
" return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
"}\n"); "}\n");
printer->Print( printer->Print(*vars,
*vars, "virtual ::grpc::experimental::ServerUnaryReactor* "
"virtual void $Method$(" "$Method$(::grpc::experimental::CallbackServerContext* "
"::grpc::ServerContext* /*context*/, const $RealRequest$* /*request*/, " "/*context*/, const $RealRequest$* "
"$RealResponse$* /*response*/, " "/*request*/, $RealResponse$* /*response*/) { "
"::grpc::experimental::ServerCallbackRpcController* " "return nullptr; }\n");
"controller) { controller->Finish(::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED, \"\")); }\n");
} else if (ClientOnlyStreaming(method)) { } else if (ClientOnlyStreaming(method)) {
printer->Print( printer->Print(
*vars, *vars,
@ -941,12 +940,12 @@ void PrintHeaderServerCallbackMethodsHelper(
" abort();\n" " abort();\n"
" return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
"}\n"); "}\n");
printer->Print( printer->Print(*vars,
*vars, "virtual ::grpc::experimental::ServerReadReactor< "
"virtual ::grpc::experimental::ServerReadReactor< " "$RealRequest$>* $Method$("
"$RealRequest$, $RealResponse$>* $Method$() {\n" "::grpc::experimental::CallbackServerContext* /*context*/, "
" return new ::grpc_impl::internal::UnimplementedReadReactor<\n" "$RealResponse$* /*response*/) { "
" $RealRequest$, $RealResponse$>;}\n"); "return nullptr; }\n");
} else if (ServerOnlyStreaming(method)) { } else if (ServerOnlyStreaming(method)) {
printer->Print( printer->Print(
*vars, *vars,
@ -961,9 +960,10 @@ void PrintHeaderServerCallbackMethodsHelper(
printer->Print( printer->Print(
*vars, *vars,
"virtual ::grpc::experimental::ServerWriteReactor< " "virtual ::grpc::experimental::ServerWriteReactor< "
"$RealRequest$, $RealResponse$>* $Method$() {\n" "$RealResponse$>* "
" return new ::grpc_impl::internal::UnimplementedWriteReactor<\n" "$Method$(::grpc::experimental::CallbackServerContext* /*context*/, "
" $RealRequest$, $RealResponse$>;}\n"); "const $RealRequest$* /*request*/) { "
"return nullptr; }\n");
} else if (method->BidiStreaming()) { } else if (method->BidiStreaming()) {
printer->Print( printer->Print(
*vars, *vars,
@ -978,9 +978,9 @@ void PrintHeaderServerCallbackMethodsHelper(
printer->Print( printer->Print(
*vars, *vars,
"virtual ::grpc::experimental::ServerBidiReactor< " "virtual ::grpc::experimental::ServerBidiReactor< "
"$RealRequest$, $RealResponse$>* $Method$() {\n" "$RealRequest$, $RealResponse$>* "
" return new ::grpc_impl::internal::UnimplementedBidiReactor<\n" "$Method$(::grpc::experimental::CallbackServerContext* /*context*/) { "
" $RealRequest$, $RealResponse$>;}\n"); "return nullptr; }\n");
} }
} }
@ -1011,14 +1011,11 @@ void PrintHeaderServerMethodCallback(
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackUnaryHandler< " " new ::grpc_impl::internal::CallbackUnaryHandler< "
"$RealRequest$, $RealResponse$>(\n" "$RealRequest$, $RealResponse$>(\n"
" [this](::grpc::ServerContext* context,\n" " [this](::grpc::experimental::CallbackServerContext* context, "
" const $RealRequest$* request,\n" "const $RealRequest$* "
" $RealResponse$* response,\n" "request, "
" ::grpc::experimental::ServerCallbackRpcController* " "$RealResponse$* response) { "
"controller) {\n" "return this->$Method$(context, request, response); }));}\n");
" return this->$"
"Method$(context, request, response, controller);\n"
" }));\n}\n");
printer->Print(*vars, printer->Print(*vars,
"void SetMessageAllocatorFor_$Method$(\n" "void SetMessageAllocatorFor_$Method$(\n"
" ::grpc::experimental::MessageAllocator< " " ::grpc::experimental::MessageAllocator< "
@ -1033,21 +1030,28 @@ void PrintHeaderServerMethodCallback(
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackClientStreamingHandler< " " new ::grpc_impl::internal::CallbackClientStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n" "$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)) { } else if (ServerOnlyStreaming(method)) {
printer->Print( printer->Print(
*vars, *vars,
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackServerStreamingHandler< " " new ::grpc_impl::internal::CallbackServerStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n" "$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()) { } else if (method->BidiStreaming()) {
printer->Print( printer->Print(
*vars, *vars,
" ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackBidiHandler< " " new ::grpc_impl::internal::CallbackBidiHandler< "
"$RealRequest$, $RealResponse$>(\n" "$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, "}\n");
printer->Print(*vars, printer->Print(*vars,
@ -1086,35 +1090,39 @@ void PrintHeaderServerMethodRawCallback(
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackUnaryHandler< " " new ::grpc_impl::internal::CallbackUnaryHandler< "
"$RealRequest$, $RealResponse$>(\n" "$RealRequest$, $RealResponse$>(\n"
" [this](::grpc::ServerContext* context,\n" " [this](::grpc::experimental::CallbackServerContext* context, "
" const $RealRequest$* request,\n" "const $RealRequest$* "
" $RealResponse$* response,\n" "request, "
" ::grpc::experimental::ServerCallbackRpcController* " "$RealResponse$* response) { return "
"controller) {\n" "this->$Method$(context, request, response); }));\n");
" this->$"
"Method$(context, request, response, controller);\n"
" }));\n");
} else if (ClientOnlyStreaming(method)) { } else if (ClientOnlyStreaming(method)) {
printer->Print( printer->Print(
*vars, *vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackClientStreamingHandler< " " new ::grpc_impl::internal::CallbackClientStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n" "$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)) { } else if (ServerOnlyStreaming(method)) {
printer->Print( printer->Print(
*vars, *vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackServerStreamingHandler< " " new ::grpc_impl::internal::CallbackServerStreamingHandler< "
"$RealRequest$, $RealResponse$>(\n" "$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()) { } else if (method->BidiStreaming()) {
printer->Print( printer->Print(
*vars, *vars,
" ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n"
" new ::grpc_impl::internal::CallbackBidiHandler< " " new ::grpc_impl::internal::CallbackBidiHandler< "
"$RealRequest$, $RealResponse$>(\n" "$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, "}\n");
printer->Print(*vars, printer->Print(*vars,
@ -1657,6 +1665,8 @@ grpc::string GetSourceIncludes(grpc_generator::File* file,
"grpcpp/impl/codegen/method_handler.h", "grpcpp/impl/codegen/method_handler.h",
"grpcpp/impl/codegen/rpc_service_method.h", "grpcpp/impl/codegen/rpc_service_method.h",
"grpcpp/impl/codegen/server_callback.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/service_type.h",
"grpcpp/impl/codegen/sync_stream.h"}; "grpcpp/impl/codegen/sync_stream.h"};
std::vector<grpc::string> headers(headers_strs, array_end(headers_strs)); std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));

@ -854,7 +854,8 @@ static void cq_end_op_for_callback(
} }
auto* functor = static_cast<grpc_experimental_completion_queue_functor*>(tag); auto* functor = static_cast<grpc_experimental_completion_queue_functor*>(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, grpc_core::ApplicationCallbackExecCtx::Enqueue(functor,
(error == GRPC_ERROR_NONE)); (error == GRPC_ERROR_NONE));
GRPC_ERROR_UNREF(error); GRPC_ERROR_UNREF(error);

@ -213,7 +213,14 @@ bool Channel::WaitForStateChangeImpl(grpc_connectivity_state last_observed,
namespace { namespace {
class ShutdownCallback : public grpc_experimental_completion_queue_functor { class ShutdownCallback : public grpc_experimental_completion_queue_functor {
public: 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 // TakeCQ takes ownership of the cq into the shutdown callback
// so that the shutdown callback will be responsible for destroying it // so that the shutdown callback will be responsible for destroying it
void TakeCQ(::grpc::CompletionQueue* cq) { cq_ = cq; } void TakeCQ(::grpc::CompletionQueue* cq) { cq_ = cq; }

@ -88,14 +88,27 @@ void ClientContext::set_credentials(
} }
} }
std::unique_ptr<ClientContext> ClientContext::FromServerContext( std::unique_ptr<ClientContext> ClientContext::FromInternalServerContext(
const grpc::ServerContext& context, PropagationOptions options) { const grpc_impl::experimental::ServerContextBase& context,
PropagationOptions options) {
std::unique_ptr<ClientContext> ctx(new ClientContext); std::unique_ptr<ClientContext> ctx(new ClientContext);
ctx->propagate_from_call_ = context.call_; ctx->propagate_from_call_ = context.call_;
ctx->propagation_options_ = options; ctx->propagation_options_ = options;
return ctx; return ctx;
} }
std::unique_ptr<ClientContext> ClientContext::FromServerContext(
const grpc_impl::ServerContext& server_context,
PropagationOptions options) {
return FromInternalServerContext(server_context, options);
}
std::unique_ptr<ClientContext> ClientContext::FromCallbackServerContext(
const grpc_impl::experimental::CallbackServerContext& server_context,
PropagationOptions options) {
return FromInternalServerContext(server_context, options);
}
void ClientContext::AddMetadata(const grpc::string& meta_key, void ClientContext::AddMetadata(const grpc::string& meta_key,
const grpc::string& meta_value) { const grpc::string& meta_value) {
send_initial_metadata_.insert(std::make_pair(meta_key, meta_value)); send_initial_metadata_.insert(std::make_pair(meta_key, meta_value));

@ -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 <grpcpp/impl/codegen/server_callback_impl.h>
#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<ClosureArg*>(void_arg);
arg->reactor->OnCancel();
arg->call->MaybeDone();
delete arg;
},
arg, nullptr),
GRPC_ERROR_NONE);
}
}
} // namespace internal
} // namespace grpc_impl

@ -258,7 +258,14 @@ bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag,
namespace { namespace {
class ShutdownCallback : public grpc_experimental_completion_queue_functor { class ShutdownCallback : public grpc_experimental_completion_queue_functor {
public: 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 // TakeCQ takes ownership of the cq into the shutdown callback
// so that the shutdown callback will be responsible for destroying it // so that the shutdown callback will be responsible for destroying it
void TakeCQ(CompletionQueue* cq) { cq_ = cq; } void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
@ -536,8 +543,9 @@ class Server::CallbackRequestBase : public grpc::internal::CompletionQueueTag {
template <class ServerContextType> template <class ServerContextType>
class Server::CallbackRequest final : public Server::CallbackRequestBase { class Server::CallbackRequest final : public Server::CallbackRequestBase {
public: public:
static_assert(std::is_base_of<grpc::ServerContext, ServerContextType>::value, static_assert(std::is_base_of<grpc::experimental::CallbackServerContext,
"ServerContextType must be derived from ServerContext"); ServerContextType>::value,
"ServerContextType must be derived from CallbackServerContext");
// The constructor needs to know the server for this callback request and its // 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 // 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<ServerContextType>* req) CallbackCallTag(Server::CallbackRequest<ServerContextType>* req)
: req_(req) { : req_(req) {
functor_run = &CallbackCallTag::StaticRun; 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 // 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 <> template <>
bool Server::CallbackRequest<grpc::ServerContext>::FinalizeResult( bool Server::CallbackRequest<grpc::experimental::CallbackServerContext>::
void** /*tag*/, bool* /*status*/) { FinalizeResult(void** /*tag*/, bool* /*status*/) {
return false; return false;
} }
template <> template <>
bool Server::CallbackRequest<grpc::GenericServerContext>::FinalizeResult( bool Server::CallbackRequest<grpc::experimental::GenericCallbackServerContext>::
void** /*tag*/, bool* status) { FinalizeResult(void** /*tag*/, bool* status) {
if (*status) { if (*status) {
// TODO(yangg) remove the copy here // TODO(yangg) remove the copy here
ctx_.method_ = grpc::StringFromCopiedSlice(call_details_->method); ctx_.method_ = grpc::StringFromCopiedSlice(call_details_->method);
@ -803,13 +818,14 @@ bool Server::CallbackRequest<grpc::GenericServerContext>::FinalizeResult(
} }
template <> template <>
const char* Server::CallbackRequest<grpc::ServerContext>::method_name() const { const char* Server::CallbackRequest<
grpc::experimental::CallbackServerContext>::method_name() const {
return method_->name(); return method_->name();
} }
template <> template <>
const char* Server::CallbackRequest<grpc::GenericServerContext>::method_name() const char* Server::CallbackRequest<
const { grpc::experimental::GenericCallbackServerContext>::method_name() const {
return ctx_.method().c_str(); 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 // TODO(vjpai): Register these dynamically based on need
for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) { for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
callback_reqs_to_start_.push_back( callback_reqs_to_start_.push_back(
new CallbackRequest<grpc::ServerContext>( new CallbackRequest<grpc::experimental::CallbackServerContext>(
this, method_index, method.get(), method_registration_tag)); this, method_index, method.get(), method_registration_tag));
} }
// Enqueue it so that it will be Request'ed later after all request // 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 // TODO(vjpai): Register these dynamically based on need
for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) { for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
callback_reqs_to_start_.push_back( callback_reqs_to_start_.push_back(
new CallbackRequest<grpc::GenericServerContext>(this, method_index, new CallbackRequest<grpc::experimental::GenericCallbackServerContext>(
nullptr, nullptr)); this, method_index, nullptr, nullptr));
} }
} }

@ -36,17 +36,19 @@
#include "src/core/lib/surface/call.h" #include "src/core/lib/surface/call.h"
namespace grpc_impl { namespace grpc_impl {
namespace experimental {
// CompletionOp // CompletionOp
class ServerContext::CompletionOp final class ServerContextBase::CompletionOp final
: public ::grpc::internal::CallOpSetInterface { : public ::grpc::internal::CallOpSetInterface {
public: public:
// initial refs: one in the server context, one in the cq // initial refs: one in the server context, one in the cq
// must ref the call before calling constructor and after deleting this // 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), : call_(*call),
reactor_(reactor), callback_controller_(callback_controller),
has_tag_(false), has_tag_(false),
tag_(nullptr), tag_(nullptr),
core_cq_tag_(this), core_cq_tag_(this),
@ -99,22 +101,6 @@ class ServerContext::CompletionOp final
tag_ = tag; tag_ = tag;
} }
void SetCancelCallback(std::function<void()> 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 set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; }
void* core_cq_tag() override { return core_cq_tag_; } void* core_cq_tag() override { return core_cq_tag_; }
@ -152,7 +138,7 @@ class ServerContext::CompletionOp final
} }
::grpc::internal::Call call_; ::grpc::internal::Call call_;
internal::ServerReactor* const reactor_; ::grpc_impl::internal::ServerCallbackCall* const callback_controller_;
bool has_tag_; bool has_tag_;
void* tag_; void* tag_;
void* core_cq_tag_; void* core_cq_tag_;
@ -160,12 +146,11 @@ class ServerContext::CompletionOp final
grpc_core::Mutex mu_; grpc_core::Mutex mu_;
bool finalized_; bool finalized_;
int cancelled_; // This is an int (not bool) because it is passed to core int cancelled_; // This is an int (not bool) because it is passed to core
std::function<void()> cancel_callback_;
bool done_intercepting_; bool done_intercepting_;
::grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_; ::grpc::internal::InterceptorBatchMethodsImpl interceptor_methods_;
}; };
void ServerContext::CompletionOp::Unref() { void ServerContextBase::CompletionOp::Unref() {
if (refs_.Unref()) { if (refs_.Unref()) {
grpc_call* call = call_.call(); grpc_call* call = call_.call();
delete this; 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; grpc_op ops;
ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER; ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER;
ops.data.recv_close_on_server.cancelled = &cancelled_; 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 */ /* No interceptors to run here */
} }
bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) { bool ServerContextBase::CompletionOp::FinalizeResult(void** tag, bool* status) {
bool ret = false; bool ret = false;
grpc_core::ReleasableMutexLock lock(&mu_); grpc_core::ReleasableMutexLock lock(&mu_);
if (done_intercepting_) { 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 // Decide whether to call the cancel callback before releasing the lock
bool call_cancel = (cancelled_ != 0); 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. // Release the lock since we may call a callback and interceptors now.
lock.Unlock(); lock.Unlock();
if (call_cancel && reactor_ != nullptr) { if (call_cancel && callback_controller_ != nullptr) {
reactor_->MaybeCallOnCancel(); callback_controller_->MaybeCallOnCancel();
} }
/* Add interception point and run through interceptors */ /* Add interception point and run through interceptors */
interceptor_methods_.AddInterceptionHookPoint( interceptor_methods_.AddInterceptionHookPoint(
@ -245,16 +220,19 @@ bool ServerContext::CompletionOp::FinalizeResult(void** tag, bool* status) {
return false; 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); Setup(deadline);
std::swap(*client_metadata_.arr(), *arr); std::swap(*client_metadata_.arr(), *arr);
} }
void ServerContext::Setup(gpr_timespec deadline) { void ServerContextBase::Setup(gpr_timespec deadline) {
completion_op_ = nullptr; completion_op_ = nullptr;
has_notify_when_done_tag_ = false; has_notify_when_done_tag_ = false;
async_notify_when_done_tag_ = nullptr; async_notify_when_done_tag_ = nullptr;
@ -267,15 +245,15 @@ void ServerContext::Setup(gpr_timespec deadline) {
rpc_info_ = nullptr; rpc_info_ = nullptr;
} }
void ServerContext::BindDeadlineAndMetadata(gpr_timespec deadline, void ServerContextBase::BindDeadlineAndMetadata(gpr_timespec deadline,
grpc_metadata_array* arr) { grpc_metadata_array* arr) {
deadline_ = deadline; deadline_ = deadline;
std::swap(*client_metadata_.arr(), *arr); std::swap(*client_metadata_.arr(), *arr);
} }
ServerContext::~ServerContext() { Clear(); } ServerContextBase::~ServerContextBase() { Clear(); }
void ServerContext::Clear() { void ServerContextBase::Clear() {
auth_context_.reset(); auth_context_.reset();
initial_metadata_.clear(); initial_metadata_.clear();
trailing_metadata_.clear(); trailing_metadata_.clear();
@ -294,11 +272,17 @@ void ServerContext::Clear() {
call_ = nullptr; call_ = nullptr;
grpc_call_unref(call); 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, void ServerContextBase::BeginCompletionOp(
std::function<void(bool)> callback, ::grpc::internal::Call* call, std::function<void(bool)> callback,
internal::ServerReactor* reactor) { ::grpc_impl::internal::ServerCallbackCall* callback_controller) {
GPR_ASSERT(!completion_op_); GPR_ASSERT(!completion_op_);
if (rpc_info_) { if (rpc_info_) {
rpc_info_->Ref(); rpc_info_->Ref();
@ -306,9 +290,10 @@ void ServerContext::BeginCompletionOp(::grpc::internal::Call* call,
grpc_call_ref(call->call()); grpc_call_ref(call->call());
completion_op_ = completion_op_ =
new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp))) new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp)))
CompletionOp(call, reactor); CompletionOp(call, callback_controller);
if (callback != nullptr) { if (callback_controller != nullptr) {
completion_tag_.Set(call->call(), std::move(callback), completion_op_); completion_tag_.Set(call->call(), std::move(callback), completion_op_,
true);
completion_op_->set_core_cq_tag(&completion_tag_); completion_op_->set_core_cq_tag(&completion_tag_);
completion_op_->set_tag(completion_op_); completion_op_->set_tag(completion_op_);
} else if (has_notify_when_done_tag_) { } else if (has_notify_when_done_tag_) {
@ -317,21 +302,21 @@ void ServerContext::BeginCompletionOp(::grpc::internal::Call* call,
call->PerformOps(completion_op_); call->PerformOps(completion_op_);
} }
::grpc::internal::CompletionQueueTag* ServerContext::GetCompletionOpTag() { ::grpc::internal::CompletionQueueTag* ServerContextBase::GetCompletionOpTag() {
return static_cast<::grpc::internal::CompletionQueueTag*>(completion_op_); return static_cast<::grpc::internal::CompletionQueueTag*>(completion_op_);
} }
void ServerContext::AddInitialMetadata(const grpc::string& key, void ServerContextBase::AddInitialMetadata(const grpc::string& key,
const grpc::string& value) { const grpc::string& value) {
initial_metadata_.insert(std::make_pair(key, value)); initial_metadata_.insert(std::make_pair(key, value));
} }
void ServerContext::AddTrailingMetadata(const grpc::string& key, void ServerContextBase::AddTrailingMetadata(const grpc::string& key,
const grpc::string& value) { const grpc::string& value) {
trailing_metadata_.insert(std::make_pair(key, value)); trailing_metadata_.insert(std::make_pair(key, value));
} }
void ServerContext::TryCancel() const { void ServerContextBase::TryCancel() const {
::grpc::internal::CancelInterceptorBatchMethods cancel_methods; ::grpc::internal::CancelInterceptorBatchMethods cancel_methods;
if (rpc_info_) { if (rpc_info_) {
for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) { for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) {
@ -345,15 +330,7 @@ void ServerContext::TryCancel() const {
} }
} }
void ServerContext::SetCancelCallback(std::function<void()> callback) { bool ServerContextBase::IsCancelled() const {
completion_op_->SetCancelCallback(std::move(callback));
}
void ServerContext::ClearCancelCallback() {
completion_op_->ClearCancelCallback();
}
bool ServerContext::IsCancelled() const {
if (completion_tag_) { if (completion_tag_) {
// When using callback API, this result is always valid. // When using callback API, this result is always valid.
return completion_op_->CheckCancelledAsync(); 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) { grpc_compression_algorithm algorithm) {
compression_algorithm_ = algorithm; compression_algorithm_ = algorithm;
const char* algorithm_name = nullptr; const char* algorithm_name = nullptr;
@ -380,7 +357,7 @@ void ServerContext::set_compression_algorithm(
AddInitialMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name); AddInitialMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name);
} }
grpc::string ServerContext::peer() const { grpc::string ServerContextBase::peer() const {
grpc::string peer; grpc::string peer;
if (call_) { if (call_) {
char* c_peer = grpc_call_get_peer(call_); char* c_peer = grpc_call_get_peer(call_);
@ -390,11 +367,11 @@ grpc::string ServerContext::peer() const {
return peer; 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_); return grpc_census_call_get_context(call_);
} }
void ServerContext::SetLoadReportingCosts( void ServerContextBase::SetLoadReportingCosts(
const std::vector<grpc::string>& cost_data) { const std::vector<grpc::string>& cost_data) {
if (call_ == nullptr) return; if (call_ == nullptr) return;
for (const auto& cost_datum : cost_data) { for (const auto& cost_datum : cost_data) {
@ -402,4 +379,5 @@ void ServerContext::SetLoadReportingCosts(
} }
} }
} // namespace experimental
} // namespace grpc_impl } // namespace grpc_impl

@ -41,6 +41,7 @@ class CQDeletingCallback : public grpc_experimental_completion_queue_functor {
public: public:
explicit CQDeletingCallback(F f) : func_(f) { explicit CQDeletingCallback(F f) : func_(f) {
functor_run = &CQDeletingCallback::Run; functor_run = &CQDeletingCallback::Run;
inlineable = false;
} }
~CQDeletingCallback() {} ~CQDeletingCallback() {}
static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
@ -62,6 +63,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
public: public:
ShutdownCallback() : done_(false) { ShutdownCallback() : done_(false) {
functor_run = &ShutdownCallback::StaticRun; functor_run = &ShutdownCallback::StaticRun;
inlineable = false;
gpr_mu_init(&mu_); gpr_mu_init(&mu_);
gpr_cv_init(&cv_); gpr_cv_init(&cv_);
} }

@ -39,6 +39,7 @@ struct CallbackContext {
explicit CallbackContext(void (*cb)( explicit CallbackContext(void (*cb)(
grpc_experimental_completion_queue_functor* functor, int success)) { grpc_experimental_completion_queue_functor* functor, int success)) {
functor.functor_run = cb; functor.functor_run = cb;
functor.inlineable = false;
gpr_event_init(&finished); gpr_event_init(&finished);
} }
}; };

@ -49,6 +49,7 @@ class SimpleFunctorForAdd : public grpc_experimental_completion_queue_functor {
friend class SimpleFunctorCheckForAdd; friend class SimpleFunctorCheckForAdd;
SimpleFunctorForAdd() { SimpleFunctorForAdd() {
functor_run = &SimpleFunctorForAdd::Run; functor_run = &SimpleFunctorForAdd::Run;
inlineable = true;
internal_next = this; internal_next = this;
internal_success = 0; internal_success = 0;
} }
@ -142,6 +143,7 @@ class SimpleFunctorCheckForAdd
public: public:
SimpleFunctorCheckForAdd(int ok, int* count) : count_(count) { SimpleFunctorCheckForAdd(int ok, int* count) : count_(count) {
functor_run = &SimpleFunctorCheckForAdd::Run; functor_run = &SimpleFunctorCheckForAdd::Run;
inlineable = true;
internal_success = ok; internal_success = ok;
} }
~SimpleFunctorCheckForAdd() {} ~SimpleFunctorCheckForAdd() {}

@ -382,6 +382,7 @@ static void test_callback(void) {
public: public:
ShutdownCallback(bool* done) : done_(done) { ShutdownCallback(bool* done) : done_(done) {
functor_run = &ShutdownCallback::Run; functor_run = &ShutdownCallback::Run;
inlineable = false;
} }
~ShutdownCallback() {} ~ShutdownCallback() {}
static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
@ -416,6 +417,8 @@ static void test_callback(void) {
public: public:
TagCallback(int* counter, int tag) : counter_(counter), tag_(tag) { TagCallback(int* counter, int tag) : counter_(counter), tag_(tag) {
functor_run = &TagCallback::Run; functor_run = &TagCallback::Run;
// Inlineable should be false since this callback takes locks.
inlineable = false;
} }
~TagCallback() {} ~TagCallback() {}
static void Run(grpc_experimental_completion_queue_functor* cb, static void Run(grpc_experimental_completion_queue_functor* cb,

@ -37,6 +37,7 @@
#include <grpcpp/impl/codegen/proto_utils.h> #include <grpcpp/impl/codegen/proto_utils.h>
#include <grpcpp/impl/codegen/rpc_method.h> #include <grpcpp/impl/codegen/rpc_method.h>
#include <grpcpp/impl/codegen/server_callback.h> #include <grpcpp/impl/codegen/server_callback.h>
#include <grpcpp/impl/codegen/server_callback_handlers.h>
#include <grpcpp/impl/codegen/server_context.h> #include <grpcpp/impl/codegen/server_context.h>
#include <grpcpp/impl/codegen/service_type.h> #include <grpcpp/impl/codegen/service_type.h>
#include <grpcpp/impl/codegen/status.h> #include <grpcpp/impl/codegen/status.h>
@ -338,13 +339,7 @@ class ServiceA final {
ExperimentalWithCallbackMethod_MethodA1() { ExperimentalWithCallbackMethod_MethodA1() {
::grpc::Service::experimental().MarkMethodCallback(0, ::grpc::Service::experimental().MarkMethodCallback(0,
new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
[this](::grpc::ServerContext* context, [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodA1(context, request, response); }));}
const ::grpc::testing::Request* request,
::grpc::testing::Response* response,
::grpc::experimental::ServerCallbackRpcController* controller) {
return this->MethodA1(context, request, response, controller);
}));
}
void SetMessageAllocatorFor_MethodA1( void SetMessageAllocatorFor_MethodA1(
::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) { ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>(
@ -359,7 +354,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 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 BaseClass> template <class BaseClass>
class ExperimentalWithCallbackMethod_MethodA2 : public BaseClass { class ExperimentalWithCallbackMethod_MethodA2 : public BaseClass {
@ -369,7 +364,7 @@ class ServiceA final {
ExperimentalWithCallbackMethod_MethodA2() { ExperimentalWithCallbackMethod_MethodA2() {
::grpc::Service::experimental().MarkMethodCallback(1, ::grpc::Service::experimental().MarkMethodCallback(1,
new ::grpc_impl::internal::CallbackClientStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( 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 { ~ExperimentalWithCallbackMethod_MethodA2() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -379,9 +374,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
} }
virtual ::grpc::experimental::ServerReadReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA2() { virtual ::grpc::experimental::ServerReadReactor< ::grpc::testing::Request>* MethodA2(::grpc::experimental::CallbackServerContext* /*context*/, ::grpc::testing::Response* /*response*/) { return nullptr; }
return new ::grpc_impl::internal::UnimplementedReadReactor<
::grpc::testing::Request, ::grpc::testing::Response>;}
}; };
template <class BaseClass> template <class BaseClass>
class ExperimentalWithCallbackMethod_MethodA3 : public BaseClass { class ExperimentalWithCallbackMethod_MethodA3 : public BaseClass {
@ -391,7 +384,7 @@ class ServiceA final {
ExperimentalWithCallbackMethod_MethodA3() { ExperimentalWithCallbackMethod_MethodA3() {
::grpc::Service::experimental().MarkMethodCallback(2, ::grpc::Service::experimental().MarkMethodCallback(2,
new ::grpc_impl::internal::CallbackServerStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( 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 { ~ExperimentalWithCallbackMethod_MethodA3() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -401,9 +394,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
} }
virtual ::grpc::experimental::ServerWriteReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA3() { virtual ::grpc::experimental::ServerWriteReactor< ::grpc::testing::Response>* MethodA3(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::testing::Request* /*request*/) { return nullptr; }
return new ::grpc_impl::internal::UnimplementedWriteReactor<
::grpc::testing::Request, ::grpc::testing::Response>;}
}; };
template <class BaseClass> template <class BaseClass>
class ExperimentalWithCallbackMethod_MethodA4 : public BaseClass { class ExperimentalWithCallbackMethod_MethodA4 : public BaseClass {
@ -413,7 +404,7 @@ class ServiceA final {
ExperimentalWithCallbackMethod_MethodA4() { ExperimentalWithCallbackMethod_MethodA4() {
::grpc::Service::experimental().MarkMethodCallback(3, ::grpc::Service::experimental().MarkMethodCallback(3,
new ::grpc_impl::internal::CallbackBidiHandler< ::grpc::testing::Request, ::grpc::testing::Response>( 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 { ~ExperimentalWithCallbackMethod_MethodA4() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -423,9 +414,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
} }
virtual ::grpc::experimental::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4() { virtual ::grpc::experimental::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4(::grpc::experimental::CallbackServerContext* /*context*/) { return nullptr; }
return new ::grpc_impl::internal::UnimplementedBidiReactor<
::grpc::testing::Request, ::grpc::testing::Response>;}
}; };
typedef ExperimentalWithCallbackMethod_MethodA1<ExperimentalWithCallbackMethod_MethodA2<ExperimentalWithCallbackMethod_MethodA3<ExperimentalWithCallbackMethod_MethodA4<Service > > > > ExperimentalCallbackService; typedef ExperimentalWithCallbackMethod_MethodA1<ExperimentalWithCallbackMethod_MethodA2<ExperimentalWithCallbackMethod_MethodA3<ExperimentalWithCallbackMethod_MethodA4<Service > > > > ExperimentalCallbackService;
template <class BaseClass> template <class BaseClass>
@ -584,12 +573,7 @@ class ServiceA final {
ExperimentalWithRawCallbackMethod_MethodA1() { ExperimentalWithRawCallbackMethod_MethodA1() {
::grpc::Service::experimental().MarkMethodRawCallback(0, ::grpc::Service::experimental().MarkMethodRawCallback(0,
new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
[this](::grpc::ServerContext* context, [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodA1(context, request, response); }));
const ::grpc::ByteBuffer* request,
::grpc::ByteBuffer* response,
::grpc::experimental::ServerCallbackRpcController* controller) {
this->MethodA1(context, request, response, controller);
}));
} }
~ExperimentalWithRawCallbackMethod_MethodA1() override { ~ExperimentalWithRawCallbackMethod_MethodA1() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -599,7 +583,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 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 BaseClass> template <class BaseClass>
class ExperimentalWithRawCallbackMethod_MethodA2 : public BaseClass { class ExperimentalWithRawCallbackMethod_MethodA2 : public BaseClass {
@ -609,7 +593,7 @@ class ServiceA final {
ExperimentalWithRawCallbackMethod_MethodA2() { ExperimentalWithRawCallbackMethod_MethodA2() {
::grpc::Service::experimental().MarkMethodRawCallback(1, ::grpc::Service::experimental().MarkMethodRawCallback(1,
new ::grpc_impl::internal::CallbackClientStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( 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 { ~ExperimentalWithRawCallbackMethod_MethodA2() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -619,9 +603,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
} }
virtual ::grpc::experimental::ServerReadReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA2() { virtual ::grpc::experimental::ServerReadReactor< ::grpc::ByteBuffer>* MethodA2(::grpc::experimental::CallbackServerContext* /*context*/, ::grpc::ByteBuffer* /*response*/) { return nullptr; }
return new ::grpc_impl::internal::UnimplementedReadReactor<
::grpc::ByteBuffer, ::grpc::ByteBuffer>;}
}; };
template <class BaseClass> template <class BaseClass>
class ExperimentalWithRawCallbackMethod_MethodA3 : public BaseClass { class ExperimentalWithRawCallbackMethod_MethodA3 : public BaseClass {
@ -631,7 +613,7 @@ class ServiceA final {
ExperimentalWithRawCallbackMethod_MethodA3() { ExperimentalWithRawCallbackMethod_MethodA3() {
::grpc::Service::experimental().MarkMethodRawCallback(2, ::grpc::Service::experimental().MarkMethodRawCallback(2,
new ::grpc_impl::internal::CallbackServerStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( 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 { ~ExperimentalWithRawCallbackMethod_MethodA3() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -641,9 +623,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
} }
virtual ::grpc::experimental::ServerWriteReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA3() { virtual ::grpc::experimental::ServerWriteReactor< ::grpc::ByteBuffer>* MethodA3(::grpc::experimental::CallbackServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/) { return nullptr; }
return new ::grpc_impl::internal::UnimplementedWriteReactor<
::grpc::ByteBuffer, ::grpc::ByteBuffer>;}
}; };
template <class BaseClass> template <class BaseClass>
class ExperimentalWithRawCallbackMethod_MethodA4 : public BaseClass { class ExperimentalWithRawCallbackMethod_MethodA4 : public BaseClass {
@ -653,7 +633,7 @@ class ServiceA final {
ExperimentalWithRawCallbackMethod_MethodA4() { ExperimentalWithRawCallbackMethod_MethodA4() {
::grpc::Service::experimental().MarkMethodRawCallback(3, ::grpc::Service::experimental().MarkMethodRawCallback(3,
new ::grpc_impl::internal::CallbackBidiHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( 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 { ~ExperimentalWithRawCallbackMethod_MethodA4() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -663,9 +643,7 @@ class ServiceA final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
} }
virtual ::grpc::experimental::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4() { virtual ::grpc::experimental::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4(::grpc::experimental::CallbackServerContext* /*context*/) { return nullptr; }
return new ::grpc_impl::internal::UnimplementedBidiReactor<
::grpc::ByteBuffer, ::grpc::ByteBuffer>;}
}; };
template <class BaseClass> template <class BaseClass>
class WithStreamedUnaryMethod_MethodA1 : public BaseClass { class WithStreamedUnaryMethod_MethodA1 : public BaseClass {
@ -816,13 +794,7 @@ class ServiceB final {
ExperimentalWithCallbackMethod_MethodB1() { ExperimentalWithCallbackMethod_MethodB1() {
::grpc::Service::experimental().MarkMethodCallback(0, ::grpc::Service::experimental().MarkMethodCallback(0,
new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(
[this](::grpc::ServerContext* context, [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) { return this->MethodB1(context, request, response); }));}
const ::grpc::testing::Request* request,
::grpc::testing::Response* response,
::grpc::experimental::ServerCallbackRpcController* controller) {
return this->MethodB1(context, request, response, controller);
}));
}
void SetMessageAllocatorFor_MethodB1( void SetMessageAllocatorFor_MethodB1(
::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) { ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) {
static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>(
@ -837,7 +809,7 @@ class ServiceB final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 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<Service > ExperimentalCallbackService; typedef ExperimentalWithCallbackMethod_MethodB1<Service > ExperimentalCallbackService;
template <class BaseClass> template <class BaseClass>
@ -885,12 +857,7 @@ class ServiceB final {
ExperimentalWithRawCallbackMethod_MethodB1() { ExperimentalWithRawCallbackMethod_MethodB1() {
::grpc::Service::experimental().MarkMethodRawCallback(0, ::grpc::Service::experimental().MarkMethodRawCallback(0,
new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>(
[this](::grpc::ServerContext* context, [this](::grpc::experimental::CallbackServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response) { return this->MethodB1(context, request, response); }));
const ::grpc::ByteBuffer* request,
::grpc::ByteBuffer* response,
::grpc::experimental::ServerCallbackRpcController* controller) {
this->MethodB1(context, request, response, controller);
}));
} }
~ExperimentalWithRawCallbackMethod_MethodB1() override { ~ExperimentalWithRawCallbackMethod_MethodB1() override {
BaseClassMustBeDerivedFromService(this); BaseClassMustBeDerivedFromService(this);
@ -900,7 +867,7 @@ class ServiceB final {
abort(); abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); 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 BaseClass> template <class BaseClass>
class WithStreamedUnaryMethod_MethodB1 : public BaseClass { class WithStreamedUnaryMethod_MethodB1 : public BaseClass {

@ -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 // Client cancels request stream after sending two messages
TEST_P(End2endTest, ClientCancelsRequestStream) { TEST_P(End2endTest, ClientCancelsRequestStream) {
MAYBE_SKIP_TEST; 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. // Test with and without a proxy.
class ProxyEnd2endTest : public End2endTest { class ProxyEnd2endTest : public End2endTest {

@ -809,14 +809,15 @@ TEST_P(HybridEnd2endTest, CallbackGenericEcho) {
EchoTestService::WithGenericMethod_Echo<TestServiceImpl> service; EchoTestService::WithGenericMethod_Echo<TestServiceImpl> service;
class GenericEchoService : public experimental::CallbackGenericService { class GenericEchoService : public experimental::CallbackGenericService {
private: 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 { class Reactor : public experimental::ServerGenericBidiReactor {
public:
Reactor() { StartRead(&request_); }
private: private:
void OnStarted(GenericServerContext* ctx) override {
ctx_ = ctx;
EXPECT_EQ(ctx->method(), "/grpc.testing.EchoTestService/Echo");
StartRead(&request_);
}
void OnDone() override { delete this; } void OnDone() override { delete this; }
void OnReadDone(bool ok) override { void OnReadDone(bool ok) override {
if (!ok) { if (!ok) {
@ -832,7 +833,6 @@ TEST_P(HybridEnd2endTest, CallbackGenericEcho) {
Finish(ok ? Status::OK Finish(ok ? Status::OK
: Status(StatusCode::UNKNOWN, "Unexpected failure")); : Status(StatusCode::UNKNOWN, "Unexpected failure"));
} }
GenericServerContext* ctx_;
ByteBuffer request_; ByteBuffer request_;
ByteBuffer response_; ByteBuffer response_;
std::atomic_int reads_complete_{0}; std::atomic_int reads_complete_{0};

@ -71,14 +71,16 @@ class CallbackTestServiceImpl
allocator_mutator_ = mutator; allocator_mutator_ = mutator;
} }
void Echo(ServerContext* /*context*/, const EchoRequest* request, experimental::ServerUnaryReactor* Echo(
EchoResponse* response, experimental::CallbackServerContext* context, const EchoRequest* request,
experimental::ServerCallbackRpcController* controller) override { EchoResponse* response) override {
response->set_message(request->message()); response->set_message(request->message());
if (allocator_mutator_) { 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: private:

@ -17,7 +17,6 @@
*/ */
#include <climits> #include <climits>
#include <thread>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
@ -28,6 +27,7 @@
#include <grpcpp/server.h> #include <grpcpp/server.h>
#include <grpcpp/server_builder.h> #include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h> #include <grpcpp/server_context.h>
#include <grpcpp/test/default_reactor_test_peer.h>
#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h" #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h" #include "src/proto/grpc/testing/echo.grpc.pb.h"
@ -50,6 +50,7 @@ using ::testing::SaveArg;
using ::testing::SetArgPointee; using ::testing::SetArgPointee;
using ::testing::WithArg; using ::testing::WithArg;
using ::testing::_; using ::testing::_;
using grpc::testing::DefaultReactorTestPeer;
using grpc::testing::EchoRequest; using grpc::testing::EchoRequest;
using grpc::testing::EchoResponse; using grpc::testing::EchoResponse;
using grpc::testing::EchoTestService; using grpc::testing::EchoTestService;
@ -161,6 +162,88 @@ class FakeClient {
EchoTestService::StubInterface* stub_; 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 { class TestServiceImpl : public EchoTestService::Service {
public: public:
Status Echo(ServerContext* /*context*/, const EchoRequest* request, Status Echo(ServerContext* /*context*/, const EchoRequest* request,

@ -18,18 +18,17 @@
#include "test/cpp/end2end/test_service_impl.h" #include "test/cpp/end2end/test_service_impl.h"
#include <string>
#include <thread>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpcpp/security/credentials.h> #include <grpcpp/security/credentials.h>
#include <grpcpp/server_context.h> #include <grpcpp/server_context.h>
#include <gtest/gtest.h>
#include <string>
#include <thread>
#include "src/proto/grpc/testing/echo.grpc.pb.h" #include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/cpp/util/string_ref_helper.h" #include "test/cpp/util/string_ref_helper.h"
#include <gtest/gtest.h>
using std::chrono::system_clock; using std::chrono::system_clock;
namespace grpc { namespace grpc {
@ -38,8 +37,8 @@ namespace {
// When echo_deadline is requested, deadline seen in the ServerContext is set in // When echo_deadline is requested, deadline seen in the ServerContext is set in
// the response in seconds. // the response in seconds.
void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request, void MaybeEchoDeadline(experimental::ServerContextBase* context,
EchoResponse* response) { const EchoRequest* request, EchoResponse* response) {
if (request->has_param() && request->param().echo_deadline()) { if (request->has_param() && request->param().echo_deadline()) {
gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME); gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
if (context->deadline() != system_clock::time_point::max()) { if (context->deadline() != system_clock::time_point::max()) {
@ -50,7 +49,7 @@ void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request,
} }
void CheckServerAuthContext( void CheckServerAuthContext(
const ServerContext* context, const experimental::ServerContextBase* context,
const grpc::string& expected_transport_security_type, const grpc::string& expected_transport_security_type,
const grpc::string& expected_client_identity) { const grpc::string& expected_client_identity) {
std::shared_ptr<const AuthContext> auth_ctx = context->auth_context(); std::shared_ptr<const AuthContext> auth_ctx = context->auth_context();
@ -76,10 +75,9 @@ int MetadataMatchCount(
const std::multimap<grpc::string_ref, grpc::string_ref>& metadata, const std::multimap<grpc::string_ref, grpc::string_ref>& metadata,
const grpc::string& key, const grpc::string& value) { const grpc::string& key, const grpc::string& value) {
int count = 0; int count = 0;
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator iter = for (const auto& metadatum : metadata) {
metadata.begin(); if (ToString(metadatum.first) == key &&
iter != metadata.end(); ++iter) { ToString(metadatum.second) == value) {
if (ToString(iter->first) == key && ToString(iter->second) == value) {
count++; count++;
} }
} }
@ -119,26 +117,12 @@ void ServerTryCancel(ServerContext* context) {
} }
} }
void ServerTryCancelNonblocking(ServerContext* context) { void ServerTryCancelNonblocking(experimental::CallbackServerContext* context) {
EXPECT_FALSE(context->IsCancelled()); EXPECT_FALSE(context->IsCancelled());
context->TryCancel(); context->TryCancel();
gpr_log(GPR_INFO, "Server called TryCancel() to cancel the request"); 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 } // namespace
Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request, 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()) { if (request->has_param() && request->param().echo_metadata_initially()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata = const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata(); context->client_metadata();
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator for (const auto& metadatum : client_metadata) {
iter = client_metadata.begin(); context->AddInitialMetadata(ToString(metadatum.first),
iter != client_metadata.end(); ++iter) { ToString(metadatum.second));
context->AddInitialMetadata(ToString(iter->first),
ToString(iter->second));
} }
} }
if (request->has_param() && request->param().echo_metadata()) { if (request->has_param() && request->param().echo_metadata()) {
const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata = const std::multimap<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata(); context->client_metadata();
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator for (const auto& metadatum : client_metadata) {
iter = client_metadata.begin(); context->AddTrailingMetadata(ToString(metadatum.first),
iter != client_metadata.end(); ++iter) { ToString(metadatum.second));
context->AddTrailingMetadata(ToString(iter->first),
ToString(iter->second));
} }
// Terminate rpc with error and debug info in trailer. // Terminate rpc with error and debug info in trailer.
if (request->param().debug_info().stack_entries_size() || if (request->param().debug_info().stack_entries_size() ||
@ -258,186 +238,6 @@ Status TestServiceImpl::CheckClientInitialMetadata(
return Status::OK; 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<StatusCode>(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<std::mutex> 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<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (std::multimap<grpc::string_ref, grpc::string_ref>::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<grpc::string_ref, grpc::string_ref>& client_metadata =
context->client_metadata();
for (std::multimap<grpc::string_ref, grpc::string_ref>::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. // Unimplemented is left unimplemented to test the returned error.
Status TestServiceImpl::RequestStream(ServerContext* context, Status TestServiceImpl::RequestStream(ServerContext* context,
@ -605,47 +405,241 @@ Status TestServiceImpl::BidiStream(
return Status::OK; return Status::OK;
} }
experimental::ServerReadReactor<EchoRequest, EchoResponse>* experimental::ServerUnaryReactor* CallbackTestServiceImpl::Echo(
CallbackTestServiceImpl::RequestStream() { experimental::CallbackServerContext* context, const EchoRequest* request,
class Reactor : public ::grpc::experimental::ServerReadReactor<EchoRequest, EchoResponse* response) {
EchoResponse> { class Reactor : public ::grpc::experimental::ServerUnaryReactor {
public: public:
Reactor() {} Reactor(CallbackTestServiceImpl* service,
void OnStarted(ServerContext* context, EchoResponse* response) override { experimental::CallbackServerContext* ctx,
// Assign ctx_ and response_ as late as possible to increase likelihood of const EchoRequest* request, EchoResponse* response)
// catching any races : 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 private:
// the server by calling ServerContext::TryCancel() depending on the void NonDelayed(bool ok) {
// value: if (!ok) {
// CANCEL_BEFORE_PROCESSING: The RPC is cancelled before the server EXPECT_TRUE(ctx_->IsCancelled());
// reads any message from the client CANCEL_DURING_PROCESSING: The RPC Finish(Status::CANCELLED);
// is cancelled while the server is reading messages from the client return;
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads }
// all the messages from the client if (req_->has_param() && req_->param().server_die()) {
server_try_cancel_ = GetIntValueFromMetadata( gpr_log(GPR_ERROR, "The request should not reach application handler.");
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); GPR_ASSERT(0);
}
if (req_->has_param() && req_->param().has_expected_error()) {
const auto& error = req_->param().expected_error();
Finish(Status(static_cast<StatusCode>(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<std::mutex> 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<grpc::string_ref, grpc::string_ref>&
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) { if (req_->has_param() && req_->param().echo_metadata()) {
ServerTryCancelNonblocking(context); const std::multimap<grpc::string_ref, grpc::string_ref>&
ctx_ = context; client_metadata = ctx_->client_metadata();
} else { for (const auto& metadatum : client_metadata) {
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { ctx_->AddTrailingMetadata(ToString(metadatum.first),
context->TryCancel(); ToString(metadatum.second));
// Don't wait for it here
} }
ctx_ = context; // Terminate rpc with error and debug info in trailer.
response_ = response; if (req_->param().debug_info().stack_entries_size() ||
StartRead(&request_); !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<EchoRequest>*
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<EchoRequest> {
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 OnDone() override { delete this; }
void OnCancel() override { void OnCancel() override {
EXPECT_TRUE(on_started_done_); EXPECT_TRUE(setup_done_);
EXPECT_TRUE(ctx_->IsCancelled()); EXPECT_TRUE(ctx_->IsCancelled());
FinishOnce(Status::CANCELLED); FinishOnce(Status::CANCELLED);
} }
@ -678,66 +672,62 @@ CallbackTestServiceImpl::RequestStream() {
} }
} }
ServerContext* ctx_; experimental::CallbackServerContext* const ctx_;
EchoResponse* response_; EchoResponse* const response_;
EchoRequest request_; EchoRequest request_;
int num_msgs_read_{0}; int num_msgs_read_{0};
int server_try_cancel_; int server_try_cancel_;
std::mutex finish_mu_; std::mutex finish_mu_;
bool finished_{false}; 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. // Return 'kNumResponseStreamMsgs' messages.
// TODO(yangg) make it generic by adding a parameter into EchoRequest // TODO(yangg) make it generic by adding a parameter into EchoRequest
experimental::ServerWriteReactor<EchoRequest, EchoResponse>* experimental::ServerWriteReactor<EchoResponse>*
CallbackTestServiceImpl::ResponseStream() { 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 class Reactor
: public ::grpc::experimental::ServerWriteReactor<EchoRequest, : public ::grpc::experimental::ServerWriteReactor<EchoResponse> {
EchoResponse> {
public: public:
Reactor() {} Reactor(experimental::CallbackServerContext* ctx,
void OnStarted(ServerContext* context, const EchoRequest* request, int server_try_cancel)
const EchoRequest* request) override { : ctx_(ctx), request_(request), server_try_cancel_(server_try_cancel) {
// 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);
server_coalescing_api_ = GetIntValueFromMetadata( server_coalescing_api_ = GetIntValueFromMetadata(
kServerUseCoalescingApi, context->client_metadata(), 0); kServerUseCoalescingApi, ctx->client_metadata(), 0);
server_responses_to_send_ = GetIntValueFromMetadata( server_responses_to_send_ = GetIntValueFromMetadata(
kServerResponseStreamsToSend, context->client_metadata(), kServerResponseStreamsToSend, ctx->client_metadata(),
kServerDefaultResponseStreamsToSend); kServerDefaultResponseStreamsToSend);
if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) { if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
ServerTryCancelNonblocking(context); ctx->TryCancel();
ctx_ = context; }
} else { if (server_try_cancel_ != CANCEL_BEFORE_PROCESSING) {
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
context->TryCancel();
}
ctx_ = context;
request_ = request;
if (num_msgs_sent_ < server_responses_to_send_) { if (num_msgs_sent_ < server_responses_to_send_) {
NextWrite(); NextWrite();
} }
} }
on_started_done_ = true; setup_done_ = true;
} }
void OnDone() override { delete this; } void OnDone() override { delete this; }
void OnCancel() override { void OnCancel() override {
EXPECT_TRUE(on_started_done_); EXPECT_TRUE(setup_done_);
EXPECT_TRUE(ctx_->IsCancelled()); EXPECT_TRUE(ctx_->IsCancelled());
FinishOnce(Status::CANCELLED); FinishOnce(Status::CANCELLED);
} }
@ -778,8 +768,8 @@ CallbackTestServiceImpl::ResponseStream() {
StartWrite(&response_); StartWrite(&response_);
} }
} }
ServerContext* ctx_; experimental::CallbackServerContext* const ctx_;
const EchoRequest* request_; const EchoRequest* const request_;
EchoResponse response_; EchoResponse response_;
int num_msgs_sent_{0}; int num_msgs_sent_{0};
int server_try_cancel_; int server_try_cancel_;
@ -787,21 +777,18 @@ CallbackTestServiceImpl::ResponseStream() {
int server_responses_to_send_; int server_responses_to_send_;
std::mutex finish_mu_; std::mutex finish_mu_;
bool finished_{false}; 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<EchoRequest, EchoResponse>* experimental::ServerBidiReactor<EchoRequest, EchoResponse>*
CallbackTestServiceImpl::BidiStream() { CallbackTestServiceImpl::BidiStream(
experimental::CallbackServerContext* context) {
class Reactor : public ::grpc::experimental::ServerBidiReactor<EchoRequest, class Reactor : public ::grpc::experimental::ServerBidiReactor<EchoRequest,
EchoResponse> { EchoResponse> {
public: public:
Reactor() {} explicit Reactor(experimental::CallbackServerContext* ctx) : ctx_(ctx) {
void OnStarted(ServerContext* context) override {
// Assign ctx_ 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 // If 'server_try_cancel' is set in the metadata, the RPC is cancelled by
// the server by calling ServerContext::TryCancel() depending on the // the server by calling ServerContext::TryCancel() depending on the
// value: // value:
@ -811,24 +798,22 @@ CallbackTestServiceImpl::BidiStream() {
// CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads // CANCEL_AFTER_PROCESSING: The RPC is cancelled after the server reads
// all the messages from the client // all the messages from the client
server_try_cancel_ = GetIntValueFromMetadata( server_try_cancel_ = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL); kServerTryCancelRequest, ctx->client_metadata(), DO_NOT_CANCEL);
server_write_last_ = GetIntValueFromMetadata( server_write_last_ = GetIntValueFromMetadata(kServerFinishAfterNReads,
kServerFinishAfterNReads, context->client_metadata(), 0); ctx->client_metadata(), 0);
if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) { if (server_try_cancel_ == CANCEL_BEFORE_PROCESSING) {
ServerTryCancelNonblocking(context); ServerTryCancelNonblocking(ctx);
ctx_ = context;
} else { } else {
if (server_try_cancel_ == CANCEL_DURING_PROCESSING) { if (server_try_cancel_ == CANCEL_DURING_PROCESSING) {
context->TryCancel(); ctx->TryCancel();
} }
ctx_ = context;
StartRead(&request_); StartRead(&request_);
} }
on_started_done_ = true; setup_done_ = true;
} }
void OnDone() override { delete this; } void OnDone() override { delete this; }
void OnCancel() override { void OnCancel() override {
EXPECT_TRUE(on_started_done_); EXPECT_TRUE(setup_done_);
EXPECT_TRUE(ctx_->IsCancelled()); EXPECT_TRUE(ctx_->IsCancelled());
FinishOnce(Status::CANCELLED); FinishOnce(Status::CANCELLED);
} }
@ -870,7 +855,7 @@ CallbackTestServiceImpl::BidiStream() {
} }
} }
ServerContext* ctx_; experimental::CallbackServerContext* const ctx_;
EchoRequest request_; EchoRequest request_;
EchoResponse response_; EchoResponse response_;
int num_msgs_read_{0}; int num_msgs_read_{0};
@ -878,10 +863,10 @@ CallbackTestServiceImpl::BidiStream() {
int server_write_last_; int server_write_last_;
std::mutex finish_mu_; std::mutex finish_mu_;
bool finished_{false}; bool finished_{false};
bool on_started_done_{false}; bool setup_done_{false};
}; };
return new Reactor; return new Reactor(context);
} }
} // namespace testing } // namespace testing

@ -33,7 +33,6 @@ namespace testing {
const int kServerDefaultResponseStreamsToSend = 3; const int kServerDefaultResponseStreamsToSend = 3;
const char* const kServerResponseStreamsToSend = "server_responses_to_send"; const char* const kServerResponseStreamsToSend = "server_responses_to_send";
const char* const kServerTryCancelRequest = "server_try_cancel"; 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 kDebugInfoTrailerKey = "debug-info-bin";
const char* const kServerFinishAfterNReads = "server_finish_after_n_reads"; const char* const kServerFinishAfterNReads = "server_finish_after_n_reads";
const char* const kServerUseCoalescingApi = "server_use_coalescing_api"; const char* const kServerUseCoalescingApi = "server_use_coalescing_api";
@ -47,13 +46,6 @@ typedef enum {
CANCEL_AFTER_PROCESSING CANCEL_AFTER_PROCESSING
} ServerTryCancelRequestPhase; } 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 { class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
public: public:
TestServiceImpl() : signal_client_(false), host_() {} TestServiceImpl() : signal_client_(false), host_() {}
@ -98,23 +90,24 @@ class CallbackTestServiceImpl
explicit CallbackTestServiceImpl(const grpc::string& host) explicit CallbackTestServiceImpl(const grpc::string& host)
: signal_client_(false), host_(new grpc::string(host)) {} : signal_client_(false), host_(new grpc::string(host)) {}
void Echo(ServerContext* context, const EchoRequest* request, experimental::ServerUnaryReactor* Echo(
EchoResponse* response, experimental::CallbackServerContext* context, const EchoRequest* request,
experimental::ServerCallbackRpcController* controller) override; EchoResponse* response) override;
void CheckClientInitialMetadata( experimental::ServerUnaryReactor* CheckClientInitialMetadata(
ServerContext* context, const SimpleRequest* request, experimental::CallbackServerContext* context, const SimpleRequest*,
SimpleResponse* response, SimpleResponse*) override;
experimental::ServerCallbackRpcController* controller) override;
experimental::ServerReadReactor<EchoRequest, EchoResponse>* RequestStream() experimental::ServerReadReactor<EchoRequest>* RequestStream(
override; experimental::CallbackServerContext* context,
EchoResponse* response) override;
experimental::ServerWriteReactor<EchoRequest, EchoResponse>* ResponseStream() experimental::ServerWriteReactor<EchoResponse>* ResponseStream(
override; experimental::CallbackServerContext* context,
const EchoRequest* request) override;
experimental::ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream() experimental::ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream(
override; experimental::CallbackServerContext* context) override;
// Unimplemented is left unimplemented to test the returned error. // Unimplemented is left unimplemented to test the returned error.
bool signal_client() { bool signal_client() {
@ -123,15 +116,6 @@ class CallbackTestServiceImpl
} }
private: 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_; bool signal_client_;
std::mutex mu_; std::mutex mu_;
std::unique_ptr<grpc::string> host_; std::unique_ptr<grpc::string> host_;

@ -160,6 +160,7 @@ class TagCallback : public grpc_experimental_completion_queue_functor {
public: public:
explicit TagCallback(int* iter) : iter_(iter) { explicit TagCallback(int* iter) : iter_(iter) {
functor_run = &TagCallback::Run; functor_run = &TagCallback::Run;
inlineable = false;
} }
~TagCallback() {} ~TagCallback() {}
static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {
@ -179,6 +180,7 @@ class ShutdownCallback : public grpc_experimental_completion_queue_functor {
public: public:
explicit ShutdownCallback(bool* done) : done_(done) { explicit ShutdownCallback(bool* done) : done_(done) {
functor_run = &ShutdownCallback::Run; functor_run = &ShutdownCallback::Run;
inlineable = false;
} }
~ShutdownCallback() {} ~ShutdownCallback() {}
static void Run(grpc_experimental_completion_queue_functor* cb, int ok) { static void Run(grpc_experimental_completion_queue_functor* cb, int ok) {

@ -67,6 +67,7 @@ class AddAnotherFunctor : public grpc_experimental_completion_queue_functor {
int num_add) int num_add)
: pool_(pool), counter_(counter), num_add_(num_add) { : pool_(pool), counter_(counter), num_add_(num_add) {
functor_run = &AddAnotherFunctor::Run; functor_run = &AddAnotherFunctor::Run;
inlineable = false;
internal_next = this; internal_next = this;
internal_success = 0; internal_success = 0;
} }
@ -130,6 +131,7 @@ class SuicideFunctorForAdd : public grpc_experimental_completion_queue_functor {
public: public:
SuicideFunctorForAdd(BlockingCounter* counter) : counter_(counter) { SuicideFunctorForAdd(BlockingCounter* counter) : counter_(counter) {
functor_run = &SuicideFunctorForAdd::Run; functor_run = &SuicideFunctorForAdd::Run;
inlineable = false;
internal_next = this; internal_next = this;
internal_success = 0; internal_success = 0;
} }
@ -182,6 +184,7 @@ class AddSelfFunctor : public grpc_experimental_completion_queue_functor {
int num_add) int num_add)
: pool_(pool), counter_(counter), num_add_(num_add) { : pool_(pool), counter_(counter), num_add_(num_add) {
functor_run = &AddSelfFunctor::Run; functor_run = &AddSelfFunctor::Run;
inlineable = false;
internal_next = this; internal_next = this;
internal_success = 0; internal_success = 0;
} }
@ -261,6 +264,7 @@ class ShortWorkFunctorForAdd
ShortWorkFunctorForAdd() { ShortWorkFunctorForAdd() {
functor_run = &ShortWorkFunctorForAdd::Run; functor_run = &ShortWorkFunctorForAdd::Run;
inlineable = false;
internal_next = this; internal_next = this;
internal_success = 0; internal_success = 0;
val_ = 0; val_ = 0;

@ -46,10 +46,9 @@ int GetIntValueFromMetadata(
} }
} // namespace } // namespace
void CallbackStreamingTestService::Echo( experimental::ServerUnaryReactor* CallbackStreamingTestService::Echo(
ServerContext* context, const EchoRequest* /*request*/, experimental::CallbackServerContext* context,
EchoResponse* response, const EchoRequest* /*request*/, EchoResponse* response) {
experimental::ServerCallbackRpcController* controller) {
int response_msgs_size = GetIntValueFromMetadata( int response_msgs_size = GetIntValueFromMetadata(
kServerMessageSize, context->client_metadata(), 0); kServerMessageSize, context->client_metadata(), 0);
if (response_msgs_size > 0) { if (response_msgs_size > 0) {
@ -57,17 +56,18 @@ void CallbackStreamingTestService::Echo(
} else { } else {
response->set_message(""); response->set_message("");
} }
controller->Finish(Status::OK); auto* reactor = context->DefaultReactor();
reactor->Finish(::grpc::Status::OK);
return reactor;
} }
experimental::ServerBidiReactor<EchoRequest, EchoResponse>* experimental::ServerBidiReactor<EchoRequest, EchoResponse>*
CallbackStreamingTestService::BidiStream() { CallbackStreamingTestService::BidiStream(
experimental::CallbackServerContext* context) {
class Reactor class Reactor
: public experimental::ServerBidiReactor<EchoRequest, EchoResponse> { : public experimental::ServerBidiReactor<EchoRequest, EchoResponse> {
public: public:
Reactor() {} explicit Reactor(experimental::CallbackServerContext* context) {
void OnStarted(ServerContext* context) override {
ctx_ = context;
message_size_ = GetIntValueFromMetadata(kServerMessageSize, message_size_ = GetIntValueFromMetadata(kServerMessageSize,
context->client_metadata(), 0); context->client_metadata(), 0);
StartRead(&request_); StartRead(&request_);
@ -100,14 +100,13 @@ CallbackStreamingTestService::BidiStream() {
} }
private: private:
ServerContext* ctx_;
EchoRequest request_; EchoRequest request_;
EchoResponse response_; EchoResponse response_;
int message_size_; int message_size_;
bool finished_{false}; bool finished_{false};
}; };
return new Reactor; return new Reactor(context);
} }
} // namespace testing } // namespace testing
} // namespace grpc } // namespace grpc

@ -36,12 +36,13 @@ class CallbackStreamingTestService
: public EchoTestService::ExperimentalCallbackService { : public EchoTestService::ExperimentalCallbackService {
public: public:
CallbackStreamingTestService() {} CallbackStreamingTestService() {}
void Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response,
experimental::ServerCallbackRpcController* controller) override;
experimental::ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream() experimental::ServerUnaryReactor* Echo(
override; experimental::CallbackServerContext* context, const EchoRequest* request,
EchoResponse* response) override;
experimental::ServerBidiReactor<EchoRequest, EchoResponse>* BidiStream(
experimental::CallbackServerContext* context) override;
}; };
} // namespace testing } // namespace testing
} // namespace grpc } // namespace grpc

@ -33,25 +33,22 @@ namespace testing {
class BenchmarkCallbackServiceImpl final class BenchmarkCallbackServiceImpl final
: public BenchmarkService::ExperimentalCallbackService { : public BenchmarkService::ExperimentalCallbackService {
public: public:
void UnaryCall( ::grpc::experimental::ServerUnaryReactor* UnaryCall(
ServerContext* /*context*/, const ::grpc::testing::SimpleRequest* request, ::grpc::experimental::CallbackServerContext* context,
::grpc::testing::SimpleResponse* response, const SimpleRequest* request, SimpleResponse* response) override {
::grpc::experimental::ServerCallbackRpcController* controller) override { auto* reactor = context->DefaultReactor();
auto s = SetResponse(request, response); reactor->Finish(SetResponse(request, response));
controller->Finish(s); return reactor;
} }
::grpc::experimental::ServerBidiReactor<::grpc::testing::SimpleRequest, ::grpc::experimental::ServerBidiReactor<::grpc::testing::SimpleRequest,
::grpc::testing::SimpleResponse>* ::grpc::testing::SimpleResponse>*
StreamingCall() override { StreamingCall(::grpc::experimental::CallbackServerContext*) override {
class Reactor class Reactor
: public ::grpc::experimental::ServerBidiReactor< : public ::grpc::experimental::ServerBidiReactor<
::grpc::testing::SimpleRequest, ::grpc::testing::SimpleResponse> { ::grpc::testing::SimpleRequest, ::grpc::testing::SimpleResponse> {
public: public:
Reactor() {} Reactor() { StartRead(&request_); }
void OnStarted(ServerContext* /*context*/) override {
StartRead(&request_);
}
void OnReadDone(bool ok) override { void OnReadDone(bool ok) override {
if (!ok) { if (!ok) {

@ -990,6 +990,7 @@ include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/serialization_traits.h \
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h \
include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context.h \
include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_context_impl.h \

@ -992,6 +992,7 @@ include/grpcpp/impl/codegen/rpc_service_method.h \
include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/security/auth_context.h \
include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/serialization_traits.h \
include/grpcpp/impl/codegen/server_callback.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_callback_impl.h \
include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context.h \
include/grpcpp/impl/codegen/server_context_impl.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.cc \
src/cpp/server/secure_server_credentials.h \ src/cpp/server/secure_server_credentials.h \
src/cpp/server/server_builder.cc \ src/cpp/server/server_builder.cc \
src/cpp/server/server_callback.cc \
src/cpp/server/server_cc.cc \ src/cpp/server/server_cc.cc \
src/cpp/server/server_context.cc \ src/cpp/server/server_context.cc \
src/cpp/server/server_credentials.cc \ src/cpp/server/server_credentials.cc \

Loading…
Cancel
Save