diff --git a/BUILD b/BUILD index 58762abfdd5..a35915d956e 100644 --- a/BUILD +++ b/BUILD @@ -268,11 +268,14 @@ GRPCXX_PUBLIC_HDRS = [ "include/grpcpp/server_posix.h", "include/grpcpp/server_posix_impl.h", "include/grpcpp/support/async_stream.h", + "include/grpcpp/support/async_stream_impl.h", "include/grpcpp/support/async_unary_call.h", + "include/grpcpp/support/async_unary_call_impl.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", "include/grpcpp/support/channel_arguments_impl.h", "include/grpcpp/support/client_callback.h", + "include/grpcpp/support/client_callback_impl.h", "include/grpcpp/support/client_interceptor.h", "include/grpcpp/support/config.h", "include/grpcpp/support/interceptor.h", @@ -280,6 +283,7 @@ GRPCXX_PUBLIC_HDRS = [ "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", "include/grpcpp/support/server_callback.h", + "include/grpcpp/support/server_callback_impl.h", "include/grpcpp/support/server_interceptor.h", "include/grpcpp/support/slice.h", "include/grpcpp/support/status.h", @@ -287,6 +291,7 @@ GRPCXX_PUBLIC_HDRS = [ "include/grpcpp/support/string_ref.h", "include/grpcpp/support/stub_options.h", "include/grpcpp/support/sync_stream.h", + "include/grpcpp/support/sync_stream_impl.h", "include/grpcpp/support/time.h", "include/grpcpp/support/validate_service_config.h", ] @@ -2161,7 +2166,9 @@ grpc_cc_library( "include/grpc++/impl/codegen/time.h", "include/grpcpp/impl/codegen/async_generic_service.h", "include/grpcpp/impl/codegen/async_stream.h", + "include/grpcpp/impl/codegen/async_stream_impl.h", "include/grpcpp/impl/codegen/async_unary_call.h", + "include/grpcpp/impl/codegen/async_unary_call_impl.h", "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", @@ -2170,6 +2177,7 @@ grpc_cc_library( "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", "include/grpcpp/impl/codegen/client_callback.h", + "include/grpcpp/impl/codegen/client_callback_impl.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_context_impl.h", "include/grpcpp/impl/codegen/client_interceptor.h", @@ -2192,6 +2200,7 @@ grpc_cc_library( "include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_callback_impl.h", "include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context_impl.h", "include/grpcpp/impl/codegen/server_interceptor.h", @@ -2203,6 +2212,7 @@ grpc_cc_library( "include/grpcpp/impl/codegen/string_ref.h", "include/grpcpp/impl/codegen/stub_options.h", "include/grpcpp/impl/codegen/sync_stream.h", + "include/grpcpp/impl/codegen/sync_stream_impl.h", "include/grpcpp/impl/codegen/time.h", ], deps = [ diff --git a/BUILD.gn b/BUILD.gn index 9d9d53f7ba5..2a7300eb25f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1050,7 +1050,9 @@ config("grpc_config") { "include/grpcpp/impl/client_unary_call.h", "include/grpcpp/impl/codegen/async_generic_service.h", "include/grpcpp/impl/codegen/async_stream.h", + "include/grpcpp/impl/codegen/async_stream_impl.h", "include/grpcpp/impl/codegen/async_unary_call.h", + "include/grpcpp/impl/codegen/async_unary_call_impl.h", "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", @@ -1059,6 +1061,7 @@ config("grpc_config") { "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", "include/grpcpp/impl/codegen/client_callback.h", + "include/grpcpp/impl/codegen/client_callback_impl.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_context_impl.h", "include/grpcpp/impl/codegen/client_interceptor.h", @@ -1087,6 +1090,7 @@ config("grpc_config") { "include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_callback_impl.h", "include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context_impl.h", "include/grpcpp/impl/codegen/server_interceptor.h", @@ -1099,6 +1103,7 @@ config("grpc_config") { "include/grpcpp/impl/codegen/stub_options.h", "include/grpcpp/impl/codegen/sync.h", "include/grpcpp/impl/codegen/sync_stream.h", + "include/grpcpp/impl/codegen/sync_stream_impl.h", "include/grpcpp/impl/codegen/time.h", "include/grpcpp/impl/grpc_library.h", "include/grpcpp/impl/method_handler_impl.h", @@ -1128,11 +1133,14 @@ config("grpc_config") { "include/grpcpp/server_posix.h", "include/grpcpp/server_posix_impl.h", "include/grpcpp/support/async_stream.h", + "include/grpcpp/support/async_stream_impl.h", "include/grpcpp/support/async_unary_call.h", + "include/grpcpp/support/async_unary_call_impl.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", "include/grpcpp/support/channel_arguments_impl.h", "include/grpcpp/support/client_callback.h", + "include/grpcpp/support/client_callback_impl.h", "include/grpcpp/support/client_interceptor.h", "include/grpcpp/support/config.h", "include/grpcpp/support/interceptor.h", @@ -1140,6 +1148,7 @@ config("grpc_config") { "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", "include/grpcpp/support/server_callback.h", + "include/grpcpp/support/server_callback_impl.h", "include/grpcpp/support/server_interceptor.h", "include/grpcpp/support/slice.h", "include/grpcpp/support/status.h", @@ -1147,6 +1156,7 @@ config("grpc_config") { "include/grpcpp/support/string_ref.h", "include/grpcpp/support/stub_options.h", "include/grpcpp/support/sync_stream.h", + "include/grpcpp/support/sync_stream_impl.h", "include/grpcpp/support/time.h", "include/grpcpp/support/validate_service_config.h", "src/core/ext/transport/inproc/inproc_transport.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 93d9fe3a163..2739181bedc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.5.1) set(PACKAGE_NAME "grpc") set(PACKAGE_VERSION "1.23.0-dev") @@ -3233,11 +3233,14 @@ foreach(_hdr include/grpcpp/server_posix.h include/grpcpp/server_posix_impl.h include/grpcpp/support/async_stream.h + include/grpcpp/support/async_stream_impl.h include/grpcpp/support/async_unary_call.h + include/grpcpp/support/async_unary_call_impl.h include/grpcpp/support/byte_buffer.h include/grpcpp/support/channel_arguments.h include/grpcpp/support/channel_arguments_impl.h include/grpcpp/support/client_callback.h + include/grpcpp/support/client_callback_impl.h include/grpcpp/support/client_interceptor.h include/grpcpp/support/config.h include/grpcpp/support/interceptor.h @@ -3245,6 +3248,7 @@ foreach(_hdr include/grpcpp/support/proto_buffer_reader.h include/grpcpp/support/proto_buffer_writer.h include/grpcpp/support/server_callback.h + include/grpcpp/support/server_callback_impl.h include/grpcpp/support/server_interceptor.h include/grpcpp/support/slice.h include/grpcpp/support/status.h @@ -3252,6 +3256,7 @@ foreach(_hdr include/grpcpp/support/string_ref.h include/grpcpp/support/stub_options.h include/grpcpp/support/sync_stream.h + include/grpcpp/support/sync_stream_impl.h include/grpcpp/support/time.h include/grpcpp/support/validate_service_config.h include/grpc/support/alloc.h @@ -3337,7 +3342,9 @@ foreach(_hdr include/grpc++/impl/codegen/time.h include/grpcpp/impl/codegen/async_generic_service.h include/grpcpp/impl/codegen/async_stream.h + include/grpcpp/impl/codegen/async_stream_impl.h include/grpcpp/impl/codegen/async_unary_call.h + include/grpcpp/impl/codegen/async_unary_call_impl.h include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h @@ -3346,6 +3353,7 @@ foreach(_hdr include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h include/grpcpp/impl/codegen/client_callback.h + include/grpcpp/impl/codegen/client_callback_impl.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_context_impl.h include/grpcpp/impl/codegen/client_interceptor.h @@ -3368,6 +3376,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h include/grpcpp/impl/codegen/server_interceptor.h @@ -3379,6 +3388,7 @@ foreach(_hdr include/grpcpp/impl/codegen/string_ref.h include/grpcpp/impl/codegen/stub_options.h include/grpcpp/impl/codegen/sync_stream.h + include/grpcpp/impl/codegen/sync_stream_impl.h include/grpcpp/impl/codegen/time.h include/grpcpp/impl/codegen/sync.h include/grpc++/impl/codegen/proto_utils.h @@ -3860,11 +3870,14 @@ foreach(_hdr include/grpcpp/server_posix.h include/grpcpp/server_posix_impl.h include/grpcpp/support/async_stream.h + include/grpcpp/support/async_stream_impl.h include/grpcpp/support/async_unary_call.h + include/grpcpp/support/async_unary_call_impl.h include/grpcpp/support/byte_buffer.h include/grpcpp/support/channel_arguments.h include/grpcpp/support/channel_arguments_impl.h include/grpcpp/support/client_callback.h + include/grpcpp/support/client_callback_impl.h include/grpcpp/support/client_interceptor.h include/grpcpp/support/config.h include/grpcpp/support/interceptor.h @@ -3872,6 +3885,7 @@ foreach(_hdr include/grpcpp/support/proto_buffer_reader.h include/grpcpp/support/proto_buffer_writer.h include/grpcpp/support/server_callback.h + include/grpcpp/support/server_callback_impl.h include/grpcpp/support/server_interceptor.h include/grpcpp/support/slice.h include/grpcpp/support/status.h @@ -3879,6 +3893,7 @@ foreach(_hdr include/grpcpp/support/string_ref.h include/grpcpp/support/stub_options.h include/grpcpp/support/sync_stream.h + include/grpcpp/support/sync_stream_impl.h include/grpcpp/support/time.h include/grpcpp/support/validate_service_config.h include/grpc/support/alloc.h @@ -3964,7 +3979,9 @@ foreach(_hdr include/grpc++/impl/codegen/time.h include/grpcpp/impl/codegen/async_generic_service.h include/grpcpp/impl/codegen/async_stream.h + include/grpcpp/impl/codegen/async_stream_impl.h include/grpcpp/impl/codegen/async_unary_call.h + include/grpcpp/impl/codegen/async_unary_call_impl.h include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h @@ -3973,6 +3990,7 @@ foreach(_hdr include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h include/grpcpp/impl/codegen/client_callback.h + include/grpcpp/impl/codegen/client_callback_impl.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_context_impl.h include/grpcpp/impl/codegen/client_interceptor.h @@ -3995,6 +4013,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h include/grpcpp/impl/codegen/server_interceptor.h @@ -4006,6 +4025,7 @@ foreach(_hdr include/grpcpp/impl/codegen/string_ref.h include/grpcpp/impl/codegen/stub_options.h include/grpcpp/impl/codegen/sync_stream.h + include/grpcpp/impl/codegen/sync_stream_impl.h include/grpcpp/impl/codegen/time.h include/grpcpp/impl/codegen/sync.h include/grpc/census.h @@ -4401,7 +4421,9 @@ foreach(_hdr include/grpc++/impl/codegen/time.h include/grpcpp/impl/codegen/async_generic_service.h include/grpcpp/impl/codegen/async_stream.h + include/grpcpp/impl/codegen/async_stream_impl.h include/grpcpp/impl/codegen/async_unary_call.h + include/grpcpp/impl/codegen/async_unary_call_impl.h include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h @@ -4410,6 +4432,7 @@ foreach(_hdr include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h include/grpcpp/impl/codegen/client_callback.h + include/grpcpp/impl/codegen/client_callback_impl.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_context_impl.h include/grpcpp/impl/codegen/client_interceptor.h @@ -4432,6 +4455,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h include/grpcpp/impl/codegen/server_interceptor.h @@ -4443,6 +4467,7 @@ foreach(_hdr include/grpcpp/impl/codegen/string_ref.h include/grpcpp/impl/codegen/stub_options.h include/grpcpp/impl/codegen/sync_stream.h + include/grpcpp/impl/codegen/sync_stream_impl.h include/grpcpp/impl/codegen/time.h include/grpc/impl/codegen/byte_buffer.h include/grpc/impl/codegen/byte_buffer_reader.h @@ -4602,7 +4627,9 @@ foreach(_hdr include/grpc++/impl/codegen/time.h include/grpcpp/impl/codegen/async_generic_service.h include/grpcpp/impl/codegen/async_stream.h + include/grpcpp/impl/codegen/async_stream_impl.h include/grpcpp/impl/codegen/async_unary_call.h + include/grpcpp/impl/codegen/async_unary_call_impl.h include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h @@ -4611,6 +4638,7 @@ foreach(_hdr include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h include/grpcpp/impl/codegen/client_callback.h + include/grpcpp/impl/codegen/client_callback_impl.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_context_impl.h include/grpcpp/impl/codegen/client_interceptor.h @@ -4633,6 +4661,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h include/grpcpp/impl/codegen/server_interceptor.h @@ -4644,6 +4673,7 @@ foreach(_hdr include/grpcpp/impl/codegen/string_ref.h include/grpcpp/impl/codegen/stub_options.h include/grpcpp/impl/codegen/sync_stream.h + include/grpcpp/impl/codegen/sync_stream_impl.h include/grpcpp/impl/codegen/time.h include/grpc/impl/codegen/byte_buffer.h include/grpc/impl/codegen/byte_buffer_reader.h @@ -4860,11 +4890,14 @@ foreach(_hdr include/grpcpp/server_posix.h include/grpcpp/server_posix_impl.h include/grpcpp/support/async_stream.h + include/grpcpp/support/async_stream_impl.h include/grpcpp/support/async_unary_call.h + include/grpcpp/support/async_unary_call_impl.h include/grpcpp/support/byte_buffer.h include/grpcpp/support/channel_arguments.h include/grpcpp/support/channel_arguments_impl.h include/grpcpp/support/client_callback.h + include/grpcpp/support/client_callback_impl.h include/grpcpp/support/client_interceptor.h include/grpcpp/support/config.h include/grpcpp/support/interceptor.h @@ -4872,6 +4905,7 @@ foreach(_hdr include/grpcpp/support/proto_buffer_reader.h include/grpcpp/support/proto_buffer_writer.h include/grpcpp/support/server_callback.h + include/grpcpp/support/server_callback_impl.h include/grpcpp/support/server_interceptor.h include/grpcpp/support/slice.h include/grpcpp/support/status.h @@ -4879,6 +4913,7 @@ foreach(_hdr include/grpcpp/support/string_ref.h include/grpcpp/support/stub_options.h include/grpcpp/support/sync_stream.h + include/grpcpp/support/sync_stream_impl.h include/grpcpp/support/time.h include/grpcpp/support/validate_service_config.h include/grpc/support/alloc.h @@ -4964,7 +4999,9 @@ foreach(_hdr include/grpc++/impl/codegen/time.h include/grpcpp/impl/codegen/async_generic_service.h include/grpcpp/impl/codegen/async_stream.h + include/grpcpp/impl/codegen/async_stream_impl.h include/grpcpp/impl/codegen/async_unary_call.h + include/grpcpp/impl/codegen/async_unary_call_impl.h include/grpcpp/impl/codegen/byte_buffer.h include/grpcpp/impl/codegen/call.h include/grpcpp/impl/codegen/call_hook.h @@ -4973,6 +5010,7 @@ foreach(_hdr include/grpcpp/impl/codegen/callback_common.h include/grpcpp/impl/codegen/channel_interface.h include/grpcpp/impl/codegen/client_callback.h + include/grpcpp/impl/codegen/client_callback_impl.h include/grpcpp/impl/codegen/client_context.h include/grpcpp/impl/codegen/client_context_impl.h include/grpcpp/impl/codegen/client_interceptor.h @@ -4995,6 +5033,7 @@ foreach(_hdr include/grpcpp/impl/codegen/security/auth_context.h include/grpcpp/impl/codegen/serialization_traits.h include/grpcpp/impl/codegen/server_callback.h + include/grpcpp/impl/codegen/server_callback_impl.h include/grpcpp/impl/codegen/server_context.h include/grpcpp/impl/codegen/server_context_impl.h include/grpcpp/impl/codegen/server_interceptor.h @@ -5006,6 +5045,7 @@ foreach(_hdr include/grpcpp/impl/codegen/string_ref.h include/grpcpp/impl/codegen/stub_options.h include/grpcpp/impl/codegen/sync_stream.h + include/grpcpp/impl/codegen/sync_stream_impl.h include/grpcpp/impl/codegen/time.h include/grpcpp/impl/codegen/sync.h ) diff --git a/Makefile b/Makefile index 32b695c24ec..8d25efd6f83 100644 --- a/Makefile +++ b/Makefile @@ -5606,11 +5606,14 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/server_posix.h \ include/grpcpp/server_posix_impl.h \ include/grpcpp/support/async_stream.h \ + include/grpcpp/support/async_stream_impl.h \ include/grpcpp/support/async_unary_call.h \ + include/grpcpp/support/async_unary_call_impl.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ include/grpcpp/support/channel_arguments_impl.h \ include/grpcpp/support/client_callback.h \ + include/grpcpp/support/client_callback_impl.h \ include/grpcpp/support/client_interceptor.h \ include/grpcpp/support/config.h \ include/grpcpp/support/interceptor.h \ @@ -5618,6 +5621,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ include/grpcpp/support/server_callback.h \ + include/grpcpp/support/server_callback_impl.h \ include/grpcpp/support/server_interceptor.h \ include/grpcpp/support/slice.h \ include/grpcpp/support/status.h \ @@ -5625,6 +5629,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/string_ref.h \ include/grpcpp/support/stub_options.h \ include/grpcpp/support/sync_stream.h \ + include/grpcpp/support/sync_stream_impl.h \ include/grpcpp/support/time.h \ include/grpcpp/support/validate_service_config.h \ include/grpc/support/alloc.h \ @@ -5710,7 +5715,9 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/codegen/time.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ + include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ + include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -5719,6 +5726,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ + include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -5741,6 +5749,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -5752,6 +5761,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync_stream.h \ + include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpcpp/impl/codegen/sync.h \ include/grpc++/impl/codegen/proto_utils.h \ @@ -6238,11 +6248,14 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/server_posix.h \ include/grpcpp/server_posix_impl.h \ include/grpcpp/support/async_stream.h \ + include/grpcpp/support/async_stream_impl.h \ include/grpcpp/support/async_unary_call.h \ + include/grpcpp/support/async_unary_call_impl.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ include/grpcpp/support/channel_arguments_impl.h \ include/grpcpp/support/client_callback.h \ + include/grpcpp/support/client_callback_impl.h \ include/grpcpp/support/client_interceptor.h \ include/grpcpp/support/config.h \ include/grpcpp/support/interceptor.h \ @@ -6250,6 +6263,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ include/grpcpp/support/server_callback.h \ + include/grpcpp/support/server_callback_impl.h \ include/grpcpp/support/server_interceptor.h \ include/grpcpp/support/slice.h \ include/grpcpp/support/status.h \ @@ -6257,6 +6271,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/string_ref.h \ include/grpcpp/support/stub_options.h \ include/grpcpp/support/sync_stream.h \ + include/grpcpp/support/sync_stream_impl.h \ include/grpcpp/support/time.h \ include/grpcpp/support/validate_service_config.h \ include/grpc/support/alloc.h \ @@ -6342,7 +6357,9 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/codegen/time.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ + include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ + include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -6351,6 +6368,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ + include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -6373,6 +6391,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -6384,6 +6403,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync_stream.h \ + include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpcpp/impl/codegen/sync.h \ include/grpc/census.h \ @@ -6751,7 +6771,9 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/codegen/time.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ + include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ + include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -6760,6 +6782,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ + include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -6782,6 +6805,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -6793,6 +6817,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync_stream.h \ + include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpc/impl/codegen/byte_buffer.h \ include/grpc/impl/codegen/byte_buffer_reader.h \ @@ -6923,7 +6948,9 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/codegen/time.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ + include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ + include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -6932,6 +6959,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ + include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -6954,6 +6982,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -6965,6 +6994,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync_stream.h \ + include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpc/impl/codegen/byte_buffer.h \ include/grpc/impl/codegen/byte_buffer_reader.h \ @@ -7187,11 +7217,14 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/server_posix.h \ include/grpcpp/server_posix_impl.h \ include/grpcpp/support/async_stream.h \ + include/grpcpp/support/async_stream_impl.h \ include/grpcpp/support/async_unary_call.h \ + include/grpcpp/support/async_unary_call_impl.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ include/grpcpp/support/channel_arguments_impl.h \ include/grpcpp/support/client_callback.h \ + include/grpcpp/support/client_callback_impl.h \ include/grpcpp/support/client_interceptor.h \ include/grpcpp/support/config.h \ include/grpcpp/support/interceptor.h \ @@ -7199,6 +7232,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ include/grpcpp/support/server_callback.h \ + include/grpcpp/support/server_callback_impl.h \ include/grpcpp/support/server_interceptor.h \ include/grpcpp/support/slice.h \ include/grpcpp/support/status.h \ @@ -7206,6 +7240,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/support/string_ref.h \ include/grpcpp/support/stub_options.h \ include/grpcpp/support/sync_stream.h \ + include/grpcpp/support/sync_stream_impl.h \ include/grpcpp/support/time.h \ include/grpcpp/support/validate_service_config.h \ include/grpc/support/alloc.h \ @@ -7291,7 +7326,9 @@ PUBLIC_HEADERS_CXX += \ include/grpc++/impl/codegen/time.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ + include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ + include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -7300,6 +7337,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ + include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -7322,6 +7360,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ + include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -7333,6 +7372,7 @@ PUBLIC_HEADERS_CXX += \ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync_stream.h \ + include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpcpp/impl/codegen/sync.h \ diff --git a/build.yaml b/build.yaml index 4faddbc648a..0fe9f7dada3 100644 --- a/build.yaml +++ b/build.yaml @@ -1255,7 +1255,9 @@ filegroups: - include/grpc++/impl/codegen/time.h - include/grpcpp/impl/codegen/async_generic_service.h - include/grpcpp/impl/codegen/async_stream.h + - include/grpcpp/impl/codegen/async_stream_impl.h - include/grpcpp/impl/codegen/async_unary_call.h + - include/grpcpp/impl/codegen/async_unary_call_impl.h - include/grpcpp/impl/codegen/byte_buffer.h - include/grpcpp/impl/codegen/call.h - include/grpcpp/impl/codegen/call_hook.h @@ -1264,6 +1266,7 @@ filegroups: - include/grpcpp/impl/codegen/callback_common.h - include/grpcpp/impl/codegen/channel_interface.h - include/grpcpp/impl/codegen/client_callback.h + - include/grpcpp/impl/codegen/client_callback_impl.h - include/grpcpp/impl/codegen/client_context.h - include/grpcpp/impl/codegen/client_context_impl.h - include/grpcpp/impl/codegen/client_interceptor.h @@ -1286,6 +1289,7 @@ filegroups: - include/grpcpp/impl/codegen/security/auth_context.h - include/grpcpp/impl/codegen/serialization_traits.h - include/grpcpp/impl/codegen/server_callback.h + - include/grpcpp/impl/codegen/server_callback_impl.h - include/grpcpp/impl/codegen/server_context.h - include/grpcpp/impl/codegen/server_context_impl.h - include/grpcpp/impl/codegen/server_interceptor.h @@ -1297,6 +1301,7 @@ filegroups: - include/grpcpp/impl/codegen/string_ref.h - include/grpcpp/impl/codegen/stub_options.h - include/grpcpp/impl/codegen/sync_stream.h + - include/grpcpp/impl/codegen/sync_stream_impl.h - include/grpcpp/impl/codegen/time.h uses: - grpc_codegen @@ -1415,11 +1420,14 @@ filegroups: - include/grpcpp/server_posix.h - include/grpcpp/server_posix_impl.h - include/grpcpp/support/async_stream.h + - include/grpcpp/support/async_stream_impl.h - include/grpcpp/support/async_unary_call.h + - include/grpcpp/support/async_unary_call_impl.h - include/grpcpp/support/byte_buffer.h - include/grpcpp/support/channel_arguments.h - include/grpcpp/support/channel_arguments_impl.h - include/grpcpp/support/client_callback.h + - include/grpcpp/support/client_callback_impl.h - include/grpcpp/support/client_interceptor.h - include/grpcpp/support/config.h - include/grpcpp/support/interceptor.h @@ -1427,6 +1435,7 @@ filegroups: - include/grpcpp/support/proto_buffer_reader.h - include/grpcpp/support/proto_buffer_writer.h - include/grpcpp/support/server_callback.h + - include/grpcpp/support/server_callback_impl.h - include/grpcpp/support/server_interceptor.h - include/grpcpp/support/slice.h - include/grpcpp/support/status.h @@ -1434,6 +1443,7 @@ filegroups: - include/grpcpp/support/string_ref.h - include/grpcpp/support/stub_options.h - include/grpcpp/support/sync_stream.h + - include/grpcpp/support/sync_stream_impl.h - include/grpcpp/support/time.h - include/grpcpp/support/validate_service_config.h headers: diff --git a/examples/cpp/helloworld/CMakeLists.txt b/examples/cpp/helloworld/CMakeLists.txt index d0f705f6d99..146ee814261 100644 --- a/examples/cpp/helloworld/CMakeLists.txt +++ b/examples/cpp/helloworld/CMakeLists.txt @@ -17,7 +17,7 @@ # See cmake_externalproject/CMakeLists.txt for all-in-one cmake build # that automatically builds all the dependencies before building helloworld. -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.5.1) project(HelloWorld C CXX) diff --git a/examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt b/examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt index 9fbdf071a8d..0d8470a8d68 100644 --- a/examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt +++ b/examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt @@ -20,7 +20,7 @@ # including the "helloworld" project itself. # See https://blog.kitware.com/cmake-superbuilds-git-submodules/ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.5.1) # Project project(HelloWorld-SuperBuild C CXX) diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index fc13e792240..d2680ee5d03 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -129,11 +129,14 @@ Pod::Spec.new do |s| 'include/grpcpp/server_posix.h', 'include/grpcpp/server_posix_impl.h', 'include/grpcpp/support/async_stream.h', + 'include/grpcpp/support/async_stream_impl.h', 'include/grpcpp/support/async_unary_call.h', + 'include/grpcpp/support/async_unary_call_impl.h', 'include/grpcpp/support/byte_buffer.h', 'include/grpcpp/support/channel_arguments.h', 'include/grpcpp/support/channel_arguments_impl.h', 'include/grpcpp/support/client_callback.h', + 'include/grpcpp/support/client_callback_impl.h', 'include/grpcpp/support/client_interceptor.h', 'include/grpcpp/support/config.h', 'include/grpcpp/support/interceptor.h', @@ -141,6 +144,7 @@ Pod::Spec.new do |s| 'include/grpcpp/support/proto_buffer_reader.h', 'include/grpcpp/support/proto_buffer_writer.h', 'include/grpcpp/support/server_callback.h', + 'include/grpcpp/support/server_callback_impl.h', 'include/grpcpp/support/server_interceptor.h', 'include/grpcpp/support/slice.h', 'include/grpcpp/support/status.h', @@ -148,11 +152,14 @@ Pod::Spec.new do |s| 'include/grpcpp/support/string_ref.h', 'include/grpcpp/support/stub_options.h', 'include/grpcpp/support/sync_stream.h', + 'include/grpcpp/support/sync_stream_impl.h', 'include/grpcpp/support/time.h', 'include/grpcpp/support/validate_service_config.h', 'include/grpcpp/impl/codegen/async_generic_service.h', 'include/grpcpp/impl/codegen/async_stream.h', + 'include/grpcpp/impl/codegen/async_stream_impl.h', 'include/grpcpp/impl/codegen/async_unary_call.h', + 'include/grpcpp/impl/codegen/async_unary_call_impl.h', 'include/grpcpp/impl/codegen/byte_buffer.h', 'include/grpcpp/impl/codegen/call.h', 'include/grpcpp/impl/codegen/call_hook.h', @@ -161,6 +168,7 @@ Pod::Spec.new do |s| 'include/grpcpp/impl/codegen/callback_common.h', 'include/grpcpp/impl/codegen/channel_interface.h', 'include/grpcpp/impl/codegen/client_callback.h', + 'include/grpcpp/impl/codegen/client_callback_impl.h', 'include/grpcpp/impl/codegen/client_context.h', 'include/grpcpp/impl/codegen/client_context_impl.h', 'include/grpcpp/impl/codegen/client_interceptor.h', @@ -183,6 +191,7 @@ Pod::Spec.new do |s| 'include/grpcpp/impl/codegen/security/auth_context.h', 'include/grpcpp/impl/codegen/serialization_traits.h', 'include/grpcpp/impl/codegen/server_callback.h', + 'include/grpcpp/impl/codegen/server_callback_impl.h', 'include/grpcpp/impl/codegen/server_context.h', 'include/grpcpp/impl/codegen/server_context_impl.h', 'include/grpcpp/impl/codegen/server_interceptor.h', @@ -194,6 +203,7 @@ Pod::Spec.new do |s| 'include/grpcpp/impl/codegen/string_ref.h', 'include/grpcpp/impl/codegen/stub_options.h', 'include/grpcpp/impl/codegen/sync_stream.h', + 'include/grpcpp/impl/codegen/sync_stream_impl.h', 'include/grpcpp/impl/codegen/time.h', 'include/grpcpp/impl/codegen/sync.h' end diff --git a/include/grpcpp/channel_impl.h b/include/grpcpp/channel_impl.h index a7eee2b8981..9ff3118645f 100644 --- a/include/grpcpp/channel_impl.h +++ b/include/grpcpp/channel_impl.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -86,22 +86,23 @@ class Channel final : public ::grpc::ChannelInterface, ::grpc::internal::Call CreateCall(const ::grpc::internal::RpcMethod& method, ::grpc_impl::ClientContext* context, - ::grpc::CompletionQueue* cq) override; + ::grpc_impl::CompletionQueue* cq) override; void PerformOpsOnCall(::grpc::internal::CallOpSetInterface* ops, ::grpc::internal::Call* call) override; void* RegisterMethod(const char* method) override; void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, gpr_timespec deadline, - ::grpc::CompletionQueue* cq, void* tag) override; + ::grpc_impl::CompletionQueue* cq, + void* tag) override; bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, gpr_timespec deadline) override; - ::grpc::CompletionQueue* CallbackCQ() override; + ::grpc_impl::CompletionQueue* CallbackCQ() override; ::grpc::internal::Call CreateCallInternal( const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, ::grpc::CompletionQueue* cq, + ::grpc_impl::ClientContext* context, ::grpc_impl::CompletionQueue* cq, size_t interceptor_pos) override; const grpc::string host_; @@ -114,7 +115,7 @@ class Channel final : public ::grpc::ChannelInterface, // with this channel (if any). It is set on the first call to CallbackCQ(). // It is _not owned_ by the channel; ownership belongs with its internal // shutdown callback tag (invoked when the CQ is fully shutdown). - ::grpc::CompletionQueue* callback_cq_ = nullptr; + ::grpc_impl::CompletionQueue* callback_cq_ = nullptr; std::vector< std::unique_ptr<::grpc::experimental::ClientInterceptorFactoryInterface>> diff --git a/include/grpcpp/generic/generic_stub_impl.h b/include/grpcpp/generic/generic_stub_impl.h index fdbc0d0a272..e670fcaa654 100644 --- a/include/grpcpp/generic/generic_stub_impl.h +++ b/include/grpcpp/generic/generic_stub_impl.h @@ -22,17 +22,20 @@ #include #include -#include -#include +#include +#include #include -#include +#include #include +#include + namespace grpc { -typedef ClientAsyncReaderWriter +typedef ::grpc_impl::ClientAsyncReaderWriter GenericClientAsyncReaderWriter; -typedef ClientAsyncResponseReader GenericClientAsyncResponseReader; +typedef ::grpc_impl::ClientAsyncResponseReader + GenericClientAsyncResponseReader; } // namespace grpc namespace grpc_impl { class CompletionQueue; @@ -50,15 +53,15 @@ class GenericStub final { /// succeeded (i.e. the call won't proceed if the return value is nullptr). std::unique_ptr PrepareCall( grpc::ClientContext* context, const grpc::string& method, - grpc::CompletionQueue* cq); + CompletionQueue* cq); /// Setup a unary call to a named method \a method using \a context, and don't /// start it. Let it be started explicitly with StartCall. /// The return value only indicates whether or not registration of the call /// succeeded (i.e. the call won't proceed if the return value is nullptr). std::unique_ptr PrepareUnaryCall( - grpc::ClientContext* context, const grpc::string& method, - const grpc::ByteBuffer& request, grpc::CompletionQueue* cq); + grpc_impl::ClientContext* context, const grpc::string& method, + const grpc::ByteBuffer& request, CompletionQueue* cq); /// DEPRECATED for multi-threaded use /// Begin a call to a named method \a method using \a context. @@ -67,8 +70,8 @@ class GenericStub final { /// The return value only indicates whether or not registration of the call /// succeeded (i.e. the call won't proceed if the return value is nullptr). std::unique_ptr Call( - grpc::ClientContext* context, const grpc::string& method, - grpc::CompletionQueue* cq, void* tag); + grpc_impl::ClientContext* context, const grpc::string& method, + CompletionQueue* cq, void* tag); /// NOTE: class experimental_type is not part of the public API of this class /// TODO(vjpai): Move these contents to the public API of GenericStub when @@ -79,23 +82,25 @@ class GenericStub final { /// Setup and start a unary call to a named method \a method using /// \a context and specifying the \a request and \a response buffers. - void UnaryCall(grpc::ClientContext* context, const grpc::string& method, - const grpc::ByteBuffer* request, grpc::ByteBuffer* response, + void UnaryCall(grpc_impl::ClientContext* context, + const grpc::string& method, const grpc::ByteBuffer* request, + grpc::ByteBuffer* response, std::function on_completion); /// Setup and start a unary call to a named method \a method using /// \a context and specifying the \a request and \a response buffers. - void UnaryCall(grpc::ClientContext* context, const grpc::string& method, - const grpc::ByteBuffer* request, grpc::ByteBuffer* response, - grpc::experimental::ClientUnaryReactor* reactor); + void UnaryCall(grpc_impl::ClientContext* context, + const grpc::string& method, const grpc::ByteBuffer* request, + grpc::ByteBuffer* response, + grpc_impl::experimental::ClientUnaryReactor* reactor); /// Setup a call to a named method \a method using \a context and tied to /// \a reactor . Like any other bidi streaming RPC, it will not be activated /// until StartCall is invoked on its reactor. void PrepareBidiStreamingCall( - grpc::ClientContext* context, const grpc::string& method, - grpc::experimental::ClientBidiReactor* reactor); + grpc_impl::ClientContext* context, const grpc::string& method, + grpc_impl::experimental::ClientBidiReactor* reactor); private: GenericStub* stub_; diff --git a/include/grpcpp/impl/codegen/async_generic_service.h b/include/grpcpp/impl/codegen/async_generic_service.h index d8e6f49b2f3..7c720ce3c23 100644 --- a/include/grpcpp/impl/codegen/async_generic_service.h +++ b/include/grpcpp/impl/codegen/async_generic_service.h @@ -19,19 +19,21 @@ #ifndef GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H #define GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H -#include +#include #include -#include +#include struct grpc_server; namespace grpc { -typedef ServerAsyncReaderWriter +typedef ::grpc_impl::ServerAsyncReaderWriter GenericServerAsyncReaderWriter; -typedef ServerAsyncResponseWriter GenericServerAsyncResponseWriter; -typedef ServerAsyncReader GenericServerAsyncReader; -typedef ServerAsyncWriter GenericServerAsyncWriter; +typedef ::grpc_impl::ServerAsyncResponseWriter + GenericServerAsyncResponseWriter; +typedef ::grpc_impl::ServerAsyncReader + GenericServerAsyncReader; +typedef ::grpc_impl::ServerAsyncWriter GenericServerAsyncWriter; class GenericServerContext final : public ::grpc_impl::ServerContext { public: @@ -75,8 +77,9 @@ class AsyncGenericService final { void RequestCall(GenericServerContext* ctx, GenericServerAsyncReaderWriter* reader_writer, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag); + ::grpc_impl::CompletionQueue* call_cq, + ::grpc_impl::ServerCompletionQueue* notification_cq, + void* tag); private: friend class grpc_impl::Server; @@ -91,7 +94,8 @@ namespace experimental { /// GenericServerContext rather than a ServerContext. All other reaction and /// operation initiation APIs are the same as ServerBidiReactor. class ServerGenericBidiReactor - : public ServerBidiReactor { + : public ::grpc_impl::experimental::ServerBidiReactor { public: /// Similar to ServerBidiReactor::OnStarted except for argument type. /// @@ -137,8 +141,10 @@ class CallbackGenericService { private: friend class ::grpc_impl::Server; - internal::CallbackBidiHandler* Handler() { - return new internal::CallbackBidiHandler( + ::grpc_impl::internal::CallbackBidiHandler* + Handler() { + return new ::grpc_impl::internal::CallbackBidiHandler( [this] { return CreateReactor(); }); } diff --git a/include/grpcpp/impl/codegen/async_stream.h b/include/grpcpp/impl/codegen/async_stream.h index 417dceb587f..d76a8982a4b 100644 --- a/include/grpcpp/impl/codegen/async_stream.h +++ b/include/grpcpp/impl/codegen/async_stream.h @@ -19,1116 +19,76 @@ #ifndef GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H #define GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H -#include -#include -#include -#include -#include -#include +#include namespace grpc { namespace internal { -/// Common interface for all client side asynchronous streaming. -class ClientAsyncStreamingInterface { - public: - virtual ~ClientAsyncStreamingInterface() {} - /// Start the call that was set up by the constructor, but only if the - /// constructor was invoked through the "Prepare" API which doesn't actually - /// start the call - virtual void StartCall(void* tag) = 0; +typedef ::grpc_impl::internal::ClientAsyncStreamingInterface + ClientAsyncStreamingInterface; - /// Request notification of the reading of the initial metadata. Completion - /// will be notified by \a tag on the associated completion queue. - /// This call is optional, but if it is used, it cannot be used concurrently - /// with or after the \a AsyncReaderInterface::Read method. - /// - /// \param[in] tag Tag identifying this request. - virtual void ReadInitialMetadata(void* tag) = 0; - - /// Indicate that the stream is to be finished and request notification for - /// when the call has been ended. - /// Should not be used concurrently with other operations. - /// - /// It is appropriate to call this method when both: - /// * the client side has no more message to send - /// (this can be declared implicitly by calling this method, or - /// explicitly through an earlier call to the WritesDone method - /// of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or - /// \a ClientAsyncReaderWriterInterface::WritesDone). - /// * there are no more messages to be received from the server (this can - /// be known implicitly by the calling code, or explicitly from an - /// earlier call to \a AsyncReaderInterface::Read that yielded a failed - /// result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). - /// - /// The tag will be returned when either: - /// - all incoming messages have been read and the server has returned - /// a status. - /// - the server has returned a non-OK status. - /// - the call failed for some reason and the library generated a - /// status. - /// - /// Note that implementations of this method attempt to receive initial - /// metadata from the server if initial metadata hasn't yet been received. - /// - /// \param[in] tag Tag identifying this request. - /// \param[out] status To be updated with the operation status. - virtual void Finish(Status* status, void* tag) = 0; -}; - -/// An interface that yields a sequence of messages of type \a R. template -class AsyncReaderInterface { - public: - virtual ~AsyncReaderInterface() {} - - /// Read a message of type \a R into \a msg. Completion will be notified by \a - /// tag on the associated completion queue. - /// This is thread-safe with respect to \a Write or \a WritesDone methods. It - /// should not be called concurrently with other streaming APIs - /// on the same stream. It is not meaningful to call it concurrently - /// with another \a AsyncReaderInterface::Read on the same stream since reads - /// on the same stream are delivered in order. - /// - /// \param[out] msg Where to eventually store the read message. - /// \param[in] tag The tag identifying the operation. - /// - /// Side effect: note that this method attempt to receive initial metadata for - /// a stream if it hasn't yet been received. - virtual void Read(R* msg, void* tag) = 0; -}; +using AsyncReaderInterface = ::grpc_impl::internal::AsyncReaderInterface; -/// An interface that can be fed a sequence of messages of type \a W. template -class AsyncWriterInterface { - public: - virtual ~AsyncWriterInterface() {} - - /// Request the writing of \a msg with identifying tag \a tag. - /// - /// Only one write may be outstanding at any given time. This means that - /// after calling Write, one must wait to receive \a tag from the completion - /// queue BEFORE calling Write again. - /// This is thread-safe with respect to \a AsyncReaderInterface::Read - /// - /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to - /// to deallocate once Write returns. - /// - /// \param[in] msg The message to be written. - /// \param[in] tag The tag identifying the operation. - virtual void Write(const W& msg, void* tag) = 0; - - /// Request the writing of \a msg using WriteOptions \a options with - /// identifying tag \a tag. - /// - /// Only one write may be outstanding at any given time. This means that - /// after calling Write, one must wait to receive \a tag from the completion - /// queue BEFORE calling Write again. - /// WriteOptions \a options is used to set the write options of this message. - /// This is thread-safe with respect to \a AsyncReaderInterface::Read - /// - /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to - /// to deallocate once Write returns. - /// - /// \param[in] msg The message to be written. - /// \param[in] options The WriteOptions to be used to write this message. - /// \param[in] tag The tag identifying the operation. - virtual void Write(const W& msg, WriteOptions options, void* tag) = 0; - - /// Request the writing of \a msg and coalesce it with the writing - /// of trailing metadata, using WriteOptions \a options with - /// identifying tag \a tag. - /// - /// For client, WriteLast is equivalent of performing Write and - /// WritesDone in a single step. - /// For server, WriteLast buffers the \a msg. The writing of \a msg is held - /// until Finish is called, where \a msg and trailing metadata are coalesced - /// and write is initiated. Note that WriteLast can only buffer \a msg up to - /// the flow control window size. If \a msg size is larger than the window - /// size, it will be sent on wire without buffering. - /// - /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to - /// to deallocate once Write returns. - /// - /// \param[in] msg The message to be written. - /// \param[in] options The WriteOptions to be used to write this message. - /// \param[in] tag The tag identifying the operation. - void WriteLast(const W& msg, WriteOptions options, void* tag) { - Write(msg, options.set_last_message(), tag); - } -}; +using AsyncWriterInterface = ::grpc_impl::internal::AsyncWriterInterface; } // namespace internal template -class ClientAsyncReaderInterface - : public internal::ClientAsyncStreamingInterface, - public internal::AsyncReaderInterface {}; +using ClientAsyncReaderInterface = ::grpc_impl::ClientAsyncReaderInterface; -namespace internal { template -class ClientAsyncReaderFactory { - public: - /// Create a stream object. - /// Write the first request out if \a start is set. - /// \a tag will be notified on \a cq when the call has been started and - /// \a request has been written out. If \a start is not set, \a tag must be - /// nullptr and the actual call must be initiated by StartCall - /// Note that \a context will be used to fill in custom initial metadata - /// used to send to the server when starting the call. - template - static ClientAsyncReader* Create(ChannelInterface* channel, - CompletionQueue* cq, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, - const W& request, bool start, void* tag) { - ::grpc::internal::Call call = channel->CreateCall(method, context, cq); - return new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientAsyncReader))) - ClientAsyncReader(call, context, request, start, tag); - } -}; -} // namespace internal - -/// Async client-side API for doing server-streaming RPCs, -/// where the incoming message stream coming from the server has -/// messages of type \a R. -template -class ClientAsyncReader final : public ClientAsyncReaderInterface { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientAsyncReader)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void StartCall(void* tag) override { - assert(!started_); - started_ = true; - StartCallInternal(tag); - } - - /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata - /// method for semantics. - /// - /// Side effect: - /// - upon receiving initial metadata from the server, - /// the \a ClientContext associated with this call is updated, and the - /// calling code can access the received metadata through the - /// \a ClientContext. - void ReadInitialMetadata(void* tag) override { - assert(started_); - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - meta_ops_.set_output_tag(tag); - meta_ops_.RecvInitialMetadata(context_); - call_.PerformOps(&meta_ops_); - } - - void Read(R* msg, void* tag) override { - assert(started_); - read_ops_.set_output_tag(tag); - if (!context_->initial_metadata_received_) { - read_ops_.RecvInitialMetadata(context_); - } - read_ops_.RecvMessage(msg); - call_.PerformOps(&read_ops_); - } - - /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. - /// - /// Side effect: - /// - the \a ClientContext associated with this call is updated with - /// possible initial and trailing metadata received from the server. - void Finish(Status* status, void* tag) override { - assert(started_); - finish_ops_.set_output_tag(tag); - if (!context_->initial_metadata_received_) { - finish_ops_.RecvInitialMetadata(context_); - } - finish_ops_.ClientRecvStatus(context_, status); - call_.PerformOps(&finish_ops_); - } - - private: - friend class internal::ClientAsyncReaderFactory; - template - ClientAsyncReader(::grpc::internal::Call call, - ::grpc_impl::ClientContext* context, const W& request, - bool start, void* tag) - : context_(context), call_(call), started_(start) { - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(init_ops_.SendMessage(request).ok()); - init_ops_.ClientSendClose(); - if (start) { - StartCallInternal(tag); - } else { - assert(tag == nullptr); - } - } - - void StartCallInternal(void* tag) { - init_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - init_ops_.set_output_tag(tag); - call_.PerformOps(&init_ops_); - } - - ::grpc_impl::ClientContext* context_; - ::grpc::internal::Call call_; - bool started_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose> - init_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> - meta_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpRecvMessage> - read_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpClientRecvStatus> - finish_ops_; -}; - -/// Common interface for client side asynchronous writing. -template -class ClientAsyncWriterInterface - : public internal::ClientAsyncStreamingInterface, - public internal::AsyncWriterInterface { - public: - /// Signal the client is done with the writes (half-close the client stream). - /// Thread-safe with respect to \a AsyncReaderInterface::Read - /// - /// \param[in] tag The tag identifying the operation. - virtual void WritesDone(void* tag) = 0; -}; +using ClientAsyncReader = ::grpc_impl::ClientAsyncReader; -namespace internal { template -class ClientAsyncWriterFactory { - public: - /// Create a stream object. - /// Start the RPC if \a start is set - /// \a tag will be notified on \a cq when the call has been started (i.e. - /// intitial metadata sent) and \a request has been written out. - /// If \a start is not set, \a tag must be nullptr and the actual call - /// must be initiated by StartCall - /// Note that \a context will be used to fill in custom initial metadata - /// used to send to the server when starting the call. - /// \a response will be filled in with the single expected response - /// message from the server upon a successful call to the \a Finish - /// method of this instance. - template - static ClientAsyncWriter* Create(ChannelInterface* channel, - CompletionQueue* cq, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, - R* response, bool start, void* tag) { - ::grpc::internal::Call call = channel->CreateCall(method, context, cq); - return new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientAsyncWriter))) - ClientAsyncWriter(call, context, response, start, tag); - } -}; -} // namespace internal +using ClientAsyncWriterInterface = ::grpc_impl::ClientAsyncWriterInterface; -/// Async API on the client side for doing client-streaming RPCs, -/// where the outgoing message stream going to the server contains -/// messages of type \a W. template -class ClientAsyncWriter final : public ClientAsyncWriterInterface { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientAsyncWriter)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void StartCall(void* tag) override { - assert(!started_); - started_ = true; - StartCallInternal(tag); - } - - /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for - /// semantics. - /// - /// Side effect: - /// - upon receiving initial metadata from the server, the \a ClientContext - /// associated with this call is updated, and the calling code can access - /// the received metadata through the \a ClientContext. - void ReadInitialMetadata(void* tag) override { - assert(started_); - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - meta_ops_.set_output_tag(tag); - meta_ops_.RecvInitialMetadata(context_); - call_.PerformOps(&meta_ops_); - } - - void Write(const W& msg, void* tag) override { - assert(started_); - write_ops_.set_output_tag(tag); - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); - call_.PerformOps(&write_ops_); - } - - void Write(const W& msg, WriteOptions options, void* tag) override { - assert(started_); - write_ops_.set_output_tag(tag); - if (options.is_last_message()) { - options.set_buffer_hint(); - write_ops_.ClientSendClose(); - } - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); - call_.PerformOps(&write_ops_); - } - - void WritesDone(void* tag) override { - assert(started_); - write_ops_.set_output_tag(tag); - write_ops_.ClientSendClose(); - call_.PerformOps(&write_ops_); - } - - /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. - /// - /// Side effect: - /// - the \a ClientContext associated with this call is updated with - /// possible initial and trailing metadata received from the server. - /// - attempts to fill in the \a response parameter passed to this class's - /// constructor with the server's response message. - void Finish(Status* status, void* tag) override { - assert(started_); - finish_ops_.set_output_tag(tag); - if (!context_->initial_metadata_received_) { - finish_ops_.RecvInitialMetadata(context_); - } - finish_ops_.ClientRecvStatus(context_, status); - call_.PerformOps(&finish_ops_); - } - - private: - friend class internal::ClientAsyncWriterFactory; - template - ClientAsyncWriter(::grpc::internal::Call call, - ::grpc_impl::ClientContext* context, R* response, - bool start, void* tag) - : context_(context), call_(call), started_(start) { - finish_ops_.RecvMessage(response); - finish_ops_.AllowNoMessage(); - if (start) { - StartCallInternal(tag); - } else { - assert(tag == nullptr); - } - } +using ClientAsyncWriter = ::grpc_impl::ClientAsyncWriter; - void StartCallInternal(void* tag) { - write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - // if corked bit is set in context, we just keep the initial metadata - // buffered up to coalesce with later message send. No op is performed. - if (!context_->initial_metadata_corked_) { - write_ops_.set_output_tag(tag); - call_.PerformOps(&write_ops_); - } - } - - ::grpc_impl::ClientContext* context_; - ::grpc::internal::Call call_; - bool started_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> - meta_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose> - write_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpGenericRecvMessage, - ::grpc::internal::CallOpClientRecvStatus> - finish_ops_; -}; - -/// Async client-side interface for bi-directional streaming, -/// where the client-to-server message stream has messages of type \a W, -/// and the server-to-client message stream has messages of type \a R. -template -class ClientAsyncReaderWriterInterface - : public internal::ClientAsyncStreamingInterface, - public internal::AsyncWriterInterface, - public internal::AsyncReaderInterface { - public: - /// Signal the client is done with the writes (half-close the client stream). - /// Thread-safe with respect to \a AsyncReaderInterface::Read - /// - /// \param[in] tag The tag identifying the operation. - virtual void WritesDone(void* tag) = 0; -}; - -namespace internal { template -class ClientAsyncReaderWriterFactory { - public: - /// Create a stream object. - /// Start the RPC request if \a start is set. - /// \a tag will be notified on \a cq when the call has been started (i.e. - /// intitial metadata sent). If \a start is not set, \a tag must be - /// nullptr and the actual call must be initiated by StartCall - /// Note that \a context will be used to fill in custom initial metadata - /// used to send to the server when starting the call. - static ClientAsyncReaderWriter* Create( - ChannelInterface* channel, CompletionQueue* cq, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, bool start, void* tag) { - ::grpc::internal::Call call = channel->CreateCall(method, context, cq); +using ClientAsyncReaderWriterInterface = + ::grpc_impl::ClientAsyncReaderWriterInterface; - return new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientAsyncReaderWriter))) - ClientAsyncReaderWriter(call, context, start, tag); - } -}; -} // namespace internal - -/// Async client-side interface for bi-directional streaming, -/// where the outgoing message stream going to the server -/// has messages of type \a W, and the incoming message stream coming -/// from the server has messages of type \a R. template -class ClientAsyncReaderWriter final - : public ClientAsyncReaderWriterInterface { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientAsyncReaderWriter)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void StartCall(void* tag) override { - assert(!started_); - started_ = true; - StartCallInternal(tag); - } - - /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method - /// for semantics of this method. - /// - /// Side effect: - /// - upon receiving initial metadata from the server, the \a ClientContext - /// is updated with it, and then the receiving initial metadata can - /// be accessed through this \a ClientContext. - void ReadInitialMetadata(void* tag) override { - assert(started_); - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - meta_ops_.set_output_tag(tag); - meta_ops_.RecvInitialMetadata(context_); - call_.PerformOps(&meta_ops_); - } - - void Read(R* msg, void* tag) override { - assert(started_); - read_ops_.set_output_tag(tag); - if (!context_->initial_metadata_received_) { - read_ops_.RecvInitialMetadata(context_); - } - read_ops_.RecvMessage(msg); - call_.PerformOps(&read_ops_); - } - - void Write(const W& msg, void* tag) override { - assert(started_); - write_ops_.set_output_tag(tag); - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); - call_.PerformOps(&write_ops_); - } - - void Write(const W& msg, WriteOptions options, void* tag) override { - assert(started_); - write_ops_.set_output_tag(tag); - if (options.is_last_message()) { - options.set_buffer_hint(); - write_ops_.ClientSendClose(); - } - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); - call_.PerformOps(&write_ops_); - } - - void WritesDone(void* tag) override { - assert(started_); - write_ops_.set_output_tag(tag); - write_ops_.ClientSendClose(); - call_.PerformOps(&write_ops_); - } - - /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. - /// Side effect - /// - the \a ClientContext associated with this call is updated with - /// possible initial and trailing metadata sent from the server. - void Finish(Status* status, void* tag) override { - assert(started_); - finish_ops_.set_output_tag(tag); - if (!context_->initial_metadata_received_) { - finish_ops_.RecvInitialMetadata(context_); - } - finish_ops_.ClientRecvStatus(context_, status); - call_.PerformOps(&finish_ops_); - } - - private: - friend class internal::ClientAsyncReaderWriterFactory; - ClientAsyncReaderWriter(::grpc::internal::Call call, - ::grpc_impl::ClientContext* context, bool start, - void* tag) - : context_(context), call_(call), started_(start) { - if (start) { - StartCallInternal(tag); - } else { - assert(tag == nullptr); - } - } - - void StartCallInternal(void* tag) { - write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - // if corked bit is set in context, we just keep the initial metadata - // buffered up to coalesce with later message send. No op is performed. - if (!context_->initial_metadata_corked_) { - write_ops_.set_output_tag(tag); - call_.PerformOps(&write_ops_); - } - } - - ::grpc_impl::ClientContext* context_; - ::grpc::internal::Call call_; - bool started_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> - meta_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpRecvMessage> - read_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose> - write_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpClientRecvStatus> - finish_ops_; -}; +using ClientAsyncReaderWriter = ::grpc_impl::ClientAsyncReaderWriter; template -class ServerAsyncReaderInterface - : public internal::ServerAsyncStreamingInterface, - public internal::AsyncReaderInterface { - public: - /// Indicate that the stream is to be finished with a certain status code - /// and also send out \a msg response to the client. - /// Request notification for when the server has sent the response and the - /// appropriate signals to the client to end the call. - /// Should not be used concurrently with other operations. - /// - /// It is appropriate to call this method when: - /// * all messages from the client have been received (either known - /// implictly, or explicitly because a previous - /// \a AsyncReaderInterface::Read operation with a non-ok result, - /// e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). - /// - /// This operation will end when the server has finished sending out initial - /// metadata (if not sent already), response message, and status, or if - /// some failure occurred when trying to do so. - /// - /// gRPC doesn't take ownership or a reference to \a msg or \a status, so it - /// is safe to deallocate once Finish returns. - /// - /// \param[in] tag Tag identifying this request. - /// \param[in] status To be sent to the client as the result of this call. - /// \param[in] msg To be sent to the client as the response for this call. - virtual void Finish(const W& msg, const Status& status, void* tag) = 0; +using ServerAsyncReaderInterface = + ::grpc_impl::ServerAsyncReaderInterface; - /// Indicate that the stream is to be finished with a certain - /// non-OK status code. - /// Request notification for when the server has sent the appropriate - /// signals to the client to end the call. - /// Should not be used concurrently with other operations. - /// - /// This call is meant to end the call with some error, and can be called at - /// any point that the server would like to "fail" the call (though note - /// this shouldn't be called concurrently with any other "sending" call, like - /// \a AsyncWriterInterface::Write). - /// - /// This operation will end when the server has finished sending out initial - /// metadata (if not sent already), and status, or if some failure occurred - /// when trying to do so. - /// - /// gRPC doesn't take ownership or a reference to \a status, so it is safe to - /// to deallocate once FinishWithError returns. - /// - /// \param[in] tag Tag identifying this request. - /// \param[in] status To be sent to the client as the result of this call. - /// - Note: \a status must have a non-OK code. - virtual void FinishWithError(const Status& status, void* tag) = 0; -}; - -/// Async server-side API for doing client-streaming RPCs, -/// where the incoming message stream from the client has messages of type \a R, -/// and the single response message sent from the server is type \a W. template -class ServerAsyncReader final : public ServerAsyncReaderInterface { - public: - explicit ServerAsyncReader(::grpc_impl::ServerContext* ctx) - : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} - - /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. - /// - /// Implicit input parameter: - /// - The initial metadata that will be sent to the client from this op will - /// be taken from the \a ServerContext associated with the call. - void SendInitialMetadata(void* tag) override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - meta_ops_.set_output_tag(tag); - 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; - call_.PerformOps(&meta_ops_); - } - - void Read(R* msg, void* tag) override { - read_ops_.set_output_tag(tag); - read_ops_.RecvMessage(msg); - call_.PerformOps(&read_ops_); - } - - /// See the \a ServerAsyncReaderInterface.Read method for semantics - /// - /// Side effect: - /// - also sends initial metadata if not alreay sent. - /// - uses the \a ServerContext associated with this call to send possible - /// initial and trailing metadata. - /// - /// Note: \a msg is not sent if \a status has a non-OK code. - /// - /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it - /// is safe to deallocate once Finish returns. - void Finish(const W& msg, const Status& status, void* tag) override { - finish_ops_.set_output_tag(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 (status.ok()) { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, - finish_ops_.SendMessage(msg)); - } else { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); - } - call_.PerformOps(&finish_ops_); - } - - /// See the \a ServerAsyncReaderInterface.Read method for semantics - /// - /// Side effect: - /// - also sends initial metadata if not alreay sent. - /// - uses the \a ServerContext associated with this call to send possible - /// initial and trailing metadata. - /// - /// gRPC doesn't take ownership or a reference to \a status, so it is safe to - /// to deallocate once FinishWithError returns. - void FinishWithError(const Status& status, void* tag) override { - GPR_CODEGEN_ASSERT(!status.ok()); - finish_ops_.set_output_tag(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_, status); - call_.PerformOps(&finish_ops_); - } - - private: - void BindCall(::grpc::internal::Call* call) override { call_ = *call; } - - ::grpc::internal::Call call_; - ::grpc_impl::ServerContext* ctx_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> - meta_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> read_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpServerSendStatus> - finish_ops_; -}; +using ServerAsyncReader = ::grpc_impl::ServerAsyncReader; template -class ServerAsyncWriterInterface - : public internal::ServerAsyncStreamingInterface, - public internal::AsyncWriterInterface { - public: - /// Indicate that the stream is to be finished with a certain status code. - /// Request notification for when the server has sent the appropriate - /// signals to the client to end the call. - /// Should not be used concurrently with other operations. - /// - /// It is appropriate to call this method when either: - /// * all messages from the client have been received (either known - /// implictly, or explicitly because a previous \a - /// AsyncReaderInterface::Read operation with a non-ok - /// result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'. - /// * it is desired to end the call early with some non-OK status code. - /// - /// This operation will end when the server has finished sending out initial - /// metadata (if not sent already), response message, and status, or if - /// some failure occurred when trying to do so. - /// - /// gRPC doesn't take ownership or a reference to \a status, so it is safe to - /// to deallocate once Finish returns. - /// - /// \param[in] tag Tag identifying this request. - /// \param[in] status To be sent to the client as the result of this call. - virtual void Finish(const Status& status, void* tag) = 0; - - /// Request the writing of \a msg and coalesce it with trailing metadata which - /// contains \a status, using WriteOptions options with - /// identifying tag \a tag. - /// - /// WriteAndFinish is equivalent of performing WriteLast and Finish - /// in a single step. - /// - /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it - /// is safe to deallocate once WriteAndFinish returns. - /// - /// \param[in] msg The message to be written. - /// \param[in] options The WriteOptions to be used to write this message. - /// \param[in] status The Status that server returns to client. - /// \param[in] tag The tag identifying the operation. - virtual void WriteAndFinish(const W& msg, WriteOptions options, - const Status& status, void* tag) = 0; -}; +using ServerAsyncWriterInterface = ::grpc_impl::ServerAsyncWriterInterface; -/// Async server-side API for doing server streaming RPCs, -/// where the outgoing message stream from the server has messages of type \a W. template -class ServerAsyncWriter final : public ServerAsyncWriterInterface { - public: - explicit ServerAsyncWriter(::grpc_impl::ServerContext* ctx) - : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} - - /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. - /// - /// Implicit input parameter: - /// - The initial metadata that will be sent to the client from this op will - /// be taken from the \a ServerContext associated with the call. - /// - /// \param[in] tag Tag identifying this request. - void SendInitialMetadata(void* tag) override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - meta_ops_.set_output_tag(tag); - 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; - call_.PerformOps(&meta_ops_); - } - - void Write(const W& msg, void* tag) override { - write_ops_.set_output_tag(tag); - EnsureInitialMetadataSent(&write_ops_); - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); - call_.PerformOps(&write_ops_); - } +using ServerAsyncWriter = ::grpc_impl::ServerAsyncWriter; - void Write(const W& msg, WriteOptions options, void* tag) override { - write_ops_.set_output_tag(tag); - if (options.is_last_message()) { - options.set_buffer_hint(); - } - - EnsureInitialMetadataSent(&write_ops_); - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); - call_.PerformOps(&write_ops_); - } - - /// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics. - /// - /// Implicit input parameter: - /// - the \a ServerContext associated with this call is used - /// for sending trailing (and initial) metadata to the client. - /// - /// Note: \a status must have an OK code. - /// - /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it - /// is safe to deallocate once WriteAndFinish returns. - void WriteAndFinish(const W& msg, WriteOptions options, const Status& status, - void* tag) override { - write_ops_.set_output_tag(tag); - EnsureInitialMetadataSent(&write_ops_); - options.set_buffer_hint(); - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); - write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); - call_.PerformOps(&write_ops_); - } - - /// See the \a ServerAsyncWriterInterface.Finish method for semantics. - /// - /// Implicit input parameter: - /// - the \a ServerContext associated with this call is used for sending - /// trailing (and initial if not already sent) metadata to the client. - /// - /// Note: there are no restrictions are the code of - /// \a status,it may be non-OK - /// - /// gRPC doesn't take ownership or a reference to \a status, so it is safe to - /// to deallocate once Finish returns. - void Finish(const Status& status, void* tag) override { - finish_ops_.set_output_tag(tag); - EnsureInitialMetadataSent(&finish_ops_); - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); - call_.PerformOps(&finish_ops_); - } - - private: - void BindCall(::grpc::internal::Call* call) override { call_ = *call; } - - template - void EnsureInitialMetadataSent(T* ops) { - if (!ctx_->sent_initial_metadata_) { - ops->SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ops->set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - } - - ::grpc::internal::Call call_; - ::grpc_impl::ServerContext* ctx_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> - meta_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpServerSendStatus> - write_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpServerSendStatus> - finish_ops_; -}; - -/// Server-side interface for asynchronous bi-directional streaming. template -class ServerAsyncReaderWriterInterface - : public internal::ServerAsyncStreamingInterface, - public internal::AsyncWriterInterface, - public internal::AsyncReaderInterface { - public: - /// Indicate that the stream is to be finished with a certain status code. - /// Request notification for when the server has sent the appropriate - /// signals to the client to end the call. - /// Should not be used concurrently with other operations. - /// - /// It is appropriate to call this method when either: - /// * all messages from the client have been received (either known - /// implictly, or explicitly because a previous \a - /// AsyncReaderInterface::Read operation - /// with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' - /// with 'false'. - /// * it is desired to end the call early with some non-OK status code. - /// - /// This operation will end when the server has finished sending out initial - /// metadata (if not sent already), response message, and status, or if some - /// failure occurred when trying to do so. - /// - /// gRPC doesn't take ownership or a reference to \a status, so it is safe to - /// to deallocate once Finish returns. - /// - /// \param[in] tag Tag identifying this request. - /// \param[in] status To be sent to the client as the result of this call. - virtual void Finish(const Status& status, void* tag) = 0; - - /// Request the writing of \a msg and coalesce it with trailing metadata which - /// contains \a status, using WriteOptions options with - /// identifying tag \a tag. - /// - /// WriteAndFinish is equivalent of performing WriteLast and Finish in a - /// single step. - /// - /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it - /// is safe to deallocate once WriteAndFinish returns. - /// - /// \param[in] msg The message to be written. - /// \param[in] options The WriteOptions to be used to write this message. - /// \param[in] status The Status that server returns to client. - /// \param[in] tag The tag identifying the operation. - virtual void WriteAndFinish(const W& msg, WriteOptions options, - const Status& status, void* tag) = 0; -}; +using ServerAsyncReaderWriterInterface = + ::grpc_impl::ServerAsyncReaderWriterInterface; -/// Async server-side API for doing bidirectional streaming RPCs, -/// where the incoming message stream coming from the client has messages of -/// type \a R, and the outgoing message stream coming from the server has -/// messages of type \a W. template -class ServerAsyncReaderWriter final - : public ServerAsyncReaderWriterInterface { - public: - explicit ServerAsyncReaderWriter(::grpc_impl::ServerContext* ctx) - : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} - - /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. - /// - /// Implicit input parameter: - /// - The initial metadata that will be sent to the client from this op will - /// be taken from the \a ServerContext associated with the call. - /// - /// \param[in] tag Tag identifying this request. - void SendInitialMetadata(void* tag) override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - meta_ops_.set_output_tag(tag); - 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; - call_.PerformOps(&meta_ops_); - } - - void Read(R* msg, void* tag) override { - read_ops_.set_output_tag(tag); - read_ops_.RecvMessage(msg); - call_.PerformOps(&read_ops_); - } +using ServerAsyncReaderWriter = ::grpc_impl::ServerAsyncReaderWriter; - void Write(const W& msg, void* tag) override { - write_ops_.set_output_tag(tag); - EnsureInitialMetadataSent(&write_ops_); - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); - call_.PerformOps(&write_ops_); - } - - void Write(const W& msg, WriteOptions options, void* tag) override { - write_ops_.set_output_tag(tag); - if (options.is_last_message()) { - options.set_buffer_hint(); - } - EnsureInitialMetadataSent(&write_ops_); - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); - call_.PerformOps(&write_ops_); - } - - /// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish - /// method for semantics. - /// - /// Implicit input parameter: - /// - the \a ServerContext associated with this call is used - /// for sending trailing (and initial) metadata to the client. - /// - /// Note: \a status must have an OK code. - // - /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it - /// is safe to deallocate once WriteAndFinish returns. - void WriteAndFinish(const W& msg, WriteOptions options, const Status& status, - void* tag) override { - write_ops_.set_output_tag(tag); - EnsureInitialMetadataSent(&write_ops_); - options.set_buffer_hint(); - GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); - write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); - call_.PerformOps(&write_ops_); - } - - /// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics. - /// - /// Implicit input parameter: - /// - the \a ServerContext associated with this call is used for sending - /// trailing (and initial if not already sent) metadata to the client. - /// - /// Note: there are no restrictions are the code of \a status, - /// it may be non-OK - // - /// gRPC doesn't take ownership or a reference to \a status, so it is safe to - /// to deallocate once Finish returns. - void Finish(const Status& status, void* tag) override { - finish_ops_.set_output_tag(tag); - EnsureInitialMetadataSent(&finish_ops_); - - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); - call_.PerformOps(&finish_ops_); - } - - private: - friend class ::grpc_impl::Server; +namespace internal { +template +using ClientAsyncReaderFactory = + ::grpc_impl::internal::ClientAsyncReaderFactory; - void BindCall(::grpc::internal::Call* call) override { call_ = *call; } +template +using ClientAsyncWriterFactory = + ::grpc_impl::internal::ClientAsyncWriterFactory; - template - void EnsureInitialMetadataSent(T* ops) { - if (!ctx_->sent_initial_metadata_) { - ops->SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ops->set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - } +template +using ClientAsyncReaderWriterFactory = + ::grpc_impl::internal::ClientAsyncReaderWriterFactory; - ::grpc::internal::Call call_; - ::grpc_impl::ServerContext* ctx_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> - meta_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> read_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpServerSendStatus> - write_ops_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpServerSendStatus> - finish_ops_; -}; +} // namespace internal } // namespace grpc diff --git a/include/grpcpp/impl/codegen/async_stream_impl.h b/include/grpcpp/impl/codegen/async_stream_impl.h new file mode 100644 index 00000000000..633a3d5dd00 --- /dev/null +++ b/include/grpcpp/impl/codegen/async_stream_impl.h @@ -0,0 +1,1134 @@ +/* + * + * 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_ASYNC_STREAM_IMPL_H +#define GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_IMPL_H + +#include +#include +#include +#include +#include +#include + +namespace grpc_impl { + +namespace internal { +/// Common interface for all client side asynchronous streaming. +class ClientAsyncStreamingInterface { + public: + virtual ~ClientAsyncStreamingInterface() {} + + /// Start the call that was set up by the constructor, but only if the + /// constructor was invoked through the "Prepare" API which doesn't actually + /// start the call + virtual void StartCall(void* tag) = 0; + + /// Request notification of the reading of the initial metadata. Completion + /// will be notified by \a tag on the associated completion queue. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a AsyncReaderInterface::Read method. + /// + /// \param[in] tag Tag identifying this request. + virtual void ReadInitialMetadata(void* tag) = 0; + + /// Indicate that the stream is to be finished and request notification for + /// when the call has been ended. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when both: + /// * the client side has no more message to send + /// (this can be declared implicitly by calling this method, or + /// explicitly through an earlier call to the WritesDone method + /// of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or + /// \a ClientAsyncReaderWriterInterface::WritesDone). + /// * there are no more messages to be received from the server (this can + /// be known implicitly by the calling code, or explicitly from an + /// earlier call to \a AsyncReaderInterface::Read that yielded a failed + /// result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). + /// + /// The tag will be returned when either: + /// - all incoming messages have been read and the server has returned + /// a status. + /// - the server has returned a non-OK status. + /// - the call failed for some reason and the library generated a + /// status. + /// + /// Note that implementations of this method attempt to receive initial + /// metadata from the server if initial metadata hasn't yet been received. + /// + /// \param[in] tag Tag identifying this request. + /// \param[out] status To be updated with the operation status. + virtual void Finish(::grpc::Status* status, void* tag) = 0; +}; + +/// An interface that yields a sequence of messages of type \a R. +template +class AsyncReaderInterface { + public: + virtual ~AsyncReaderInterface() {} + + /// Read a message of type \a R into \a msg. Completion will be notified by \a + /// tag on the associated completion queue. + /// This is thread-safe with respect to \a Write or \a WritesDone methods. It + /// should not be called concurrently with other streaming APIs + /// on the same stream. It is not meaningful to call it concurrently + /// with another \a AsyncReaderInterface::Read on the same stream since reads + /// on the same stream are delivered in order. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + /// + /// Side effect: note that this method attempt to receive initial metadata for + /// a stream if it hasn't yet been received. + virtual void Read(R* msg, void* tag) = 0; +}; + +/// An interface that can be fed a sequence of messages of type \a W. +template +class AsyncWriterInterface { + public: + virtual ~AsyncWriterInterface() {} + + /// Request the writing of \a msg with identifying tag \a tag. + /// + /// Only one write may be outstanding at any given time. This means that + /// after calling Write, one must wait to receive \a tag from the completion + /// queue BEFORE calling Write again. + /// This is thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] tag The tag identifying the operation. + virtual void Write(const W& msg, void* tag) = 0; + + /// Request the writing of \a msg using WriteOptions \a options with + /// identifying tag \a tag. + /// + /// Only one write may be outstanding at any given time. This means that + /// after calling Write, one must wait to receive \a tag from the completion + /// queue BEFORE calling Write again. + /// WriteOptions \a options is used to set the write options of this message. + /// This is thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] tag The tag identifying the operation. + virtual void Write(const W& msg, ::grpc::WriteOptions options, void* tag) = 0; + + /// Request the writing of \a msg and coalesce it with the writing + /// of trailing metadata, using WriteOptions \a options with + /// identifying tag \a tag. + /// + /// For client, WriteLast is equivalent of performing Write and + /// WritesDone in a single step. + /// For server, WriteLast buffers the \a msg. The writing of \a msg is held + /// until Finish is called, where \a msg and trailing metadata are coalesced + /// and write is initiated. Note that WriteLast can only buffer \a msg up to + /// the flow control window size. If \a msg size is larger than the window + /// size, it will be sent on wire without buffering. + /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] tag The tag identifying the operation. + void WriteLast(const W& msg, ::grpc::WriteOptions options, void* tag) { + Write(msg, options.set_last_message(), tag); + } +}; + +} // namespace internal + +template +class ClientAsyncReaderInterface + : public internal::ClientAsyncStreamingInterface, + public internal::AsyncReaderInterface {}; + +namespace internal { +template +class ClientAsyncReaderFactory { + public: + /// Create a stream object. + /// Write the first request out if \a start is set. + /// \a tag will be notified on \a cq when the call has been started and + /// \a request has been written out. If \a start is not set, \a tag must be + /// nullptr and the actual call must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + template + static ClientAsyncReader* Create(::grpc::ChannelInterface* channel, + ::grpc_impl::CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + const W& request, bool start, void* tag) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + return new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncReader))) + ClientAsyncReader(call, context, request, start, tag); + } +}; +} // namespace internal + +/// Async client-side API for doing server-streaming RPCs, +/// where the incoming message stream coming from the server has +/// messages of type \a R. +template +class ClientAsyncReader final : public ClientAsyncReaderInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncReader)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall(void* tag) override { + assert(!started_); + started_ = true; + StartCallInternal(tag); + } + + /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata + /// method for semantics. + /// + /// Side effect: + /// - upon receiving initial metadata from the server, + /// the \a ClientContext associated with this call is updated, and the + /// calling code can access the received metadata through the + /// \a ClientContext. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + meta_ops_.set_output_tag(tag); + meta_ops_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) override { + assert(started_); + read_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + read_ops_.RecvInitialMetadata(context_); + } + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata received from the server. + void Finish(::grpc::Status* status, void* tag) override { + assert(started_); + finish_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_ops_); + } + + private: + friend class internal::ClientAsyncReaderFactory; + template + ClientAsyncReader(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, const W& request, + bool start, void* tag) + : context_(context), call_(call), started_(start) { + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(init_ops_.SendMessage(request).ok()); + init_ops_.ClientSendClose(); + if (start) { + StartCallInternal(tag); + } else { + assert(tag == nullptr); + } + } + + void StartCallInternal(void* tag) { + init_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + init_ops_.set_output_tag(tag); + call_.PerformOps(&init_ops_); + } + + ::grpc_impl::ClientContext* context_; + ::grpc::internal::Call call_; + bool started_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + init_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; +}; + +/// Common interface for client side asynchronous writing. +template +class ClientAsyncWriterInterface + : public internal::ClientAsyncStreamingInterface, + public internal::AsyncWriterInterface { + public: + /// Signal the client is done with the writes (half-close the client stream). + /// Thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// \param[in] tag The tag identifying the operation. + virtual void WritesDone(void* tag) = 0; +}; + +namespace internal { +template +class ClientAsyncWriterFactory { + public: + /// Create a stream object. + /// Start the RPC if \a start is set + /// \a tag will be notified on \a cq when the call has been started (i.e. + /// intitial metadata sent) and \a request has been written out. + /// If \a start is not set, \a tag must be nullptr and the actual call + /// must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + /// \a response will be filled in with the single expected response + /// message from the server upon a successful call to the \a Finish + /// method of this instance. + template + static ClientAsyncWriter* Create(::grpc::ChannelInterface* channel, + ::grpc_impl::CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + R* response, bool start, void* tag) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + return new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncWriter))) + ClientAsyncWriter(call, context, response, start, tag); + } +}; +} // namespace internal + +/// Async API on the client side for doing client-streaming RPCs, +/// where the outgoing message stream going to the server contains +/// messages of type \a W. +template +class ClientAsyncWriter final : public ClientAsyncWriterInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncWriter)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall(void* tag) override { + assert(!started_); + started_ = true; + StartCallInternal(tag); + } + + /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for + /// semantics. + /// + /// Side effect: + /// - upon receiving initial metadata from the server, the \a ClientContext + /// associated with this call is updated, and the calling code can access + /// the received metadata through the \a ClientContext. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + meta_ops_.set_output_tag(tag); + meta_ops_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_ops_); + } + + void Write(const W& msg, void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, ::grpc::WriteOptions options, void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WritesDone(void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + write_ops_.ClientSendClose(); + call_.PerformOps(&write_ops_); + } + + /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata received from the server. + /// - attempts to fill in the \a response parameter passed to this class's + /// constructor with the server's response message. + void Finish(::grpc::Status* status, void* tag) override { + assert(started_); + finish_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_ops_); + } + + private: + friend class internal::ClientAsyncWriterFactory; + template + ClientAsyncWriter(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, R* response, + bool start, void* tag) + : context_(context), call_(call), started_(start) { + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + if (start) { + StartCallInternal(tag); + } else { + assert(tag == nullptr); + } + } + + void StartCallInternal(void* tag) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + // if corked bit is set in context, we just keep the initial metadata + // buffered up to coalesce with later message send. No op is performed. + if (!context_->initial_metadata_corked_) { + write_ops_.set_output_tag(tag); + call_.PerformOps(&write_ops_); + } + } + + ::grpc_impl::ClientContext* context_; + ::grpc::internal::Call call_; + bool started_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpGenericRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; +}; + +/// Async client-side interface for bi-directional streaming, +/// where the client-to-server message stream has messages of type \a W, +/// and the server-to-client message stream has messages of type \a R. +template +class ClientAsyncReaderWriterInterface + : public internal::ClientAsyncStreamingInterface, + public internal::AsyncWriterInterface, + public internal::AsyncReaderInterface { + public: + /// Signal the client is done with the writes (half-close the client stream). + /// Thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// \param[in] tag The tag identifying the operation. + virtual void WritesDone(void* tag) = 0; +}; + +namespace internal { +template +class ClientAsyncReaderWriterFactory { + public: + /// Create a stream object. + /// Start the RPC request if \a start is set. + /// \a tag will be notified on \a cq when the call has been started (i.e. + /// intitial metadata sent). If \a start is not set, \a tag must be + /// nullptr and the actual call must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + static ClientAsyncReaderWriter* Create( + ::grpc::ChannelInterface* channel, ::grpc_impl::CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, bool start, void* tag) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + + return new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncReaderWriter))) + ClientAsyncReaderWriter(call, context, start, tag); + } +}; +} // namespace internal + +/// Async client-side interface for bi-directional streaming, +/// where the outgoing message stream going to the server +/// has messages of type \a W, and the incoming message stream coming +/// from the server has messages of type \a R. +template +class ClientAsyncReaderWriter final + : public ClientAsyncReaderWriterInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncReaderWriter)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall(void* tag) override { + assert(!started_); + started_ = true; + StartCallInternal(tag); + } + + /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method + /// for semantics of this method. + /// + /// Side effect: + /// - upon receiving initial metadata from the server, the \a ClientContext + /// is updated with it, and then the receiving initial metadata can + /// be accessed through this \a ClientContext. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + meta_ops_.set_output_tag(tag); + meta_ops_.RecvInitialMetadata(context_); + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) override { + assert(started_); + read_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + read_ops_.RecvInitialMetadata(context_); + } + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Write(const W& msg, void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, ::grpc::WriteOptions options, void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WritesDone(void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + write_ops_.ClientSendClose(); + call_.PerformOps(&write_ops_); + } + + /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. + /// Side effect + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata sent from the server. + void Finish(::grpc::Status* status, void* tag) override { + assert(started_); + finish_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_ops_); + } + + private: + friend class internal::ClientAsyncReaderWriterFactory; + ClientAsyncReaderWriter(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, bool start, + void* tag) + : context_(context), call_(call), started_(start) { + if (start) { + StartCallInternal(tag); + } else { + assert(tag == nullptr); + } + } + + void StartCallInternal(void* tag) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + // if corked bit is set in context, we just keep the initial metadata + // buffered up to coalesce with later message send. No op is performed. + if (!context_->initial_metadata_corked_) { + write_ops_.set_output_tag(tag); + call_.PerformOps(&write_ops_); + } + } + + ::grpc_impl::ClientContext* context_; + ::grpc::internal::Call call_; + bool started_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; +}; + +template +class ServerAsyncReaderInterface + : public ::grpc::internal::ServerAsyncStreamingInterface, + public internal::AsyncReaderInterface { + public: + /// Indicate that the stream is to be finished with a certain status code + /// and also send out \a msg response to the client. + /// Request notification for when the server has sent the response and the + /// appropriate signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when: + /// * all messages from the client have been received (either known + /// implictly, or explicitly because a previous + /// \a AsyncReaderInterface::Read operation with a non-ok result, + /// e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), response message, and status, or if + /// some failure occurred when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a msg or \a status, so it + /// is safe to deallocate once Finish returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + /// \param[in] msg To be sent to the client as the response for this call. + virtual void Finish(const W& msg, const ::grpc::Status& status, + void* tag) = 0; + + /// Indicate that the stream is to be finished with a certain + /// non-OK status code. + /// Request notification for when the server has sent the appropriate + /// signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// This call is meant to end the call with some error, and can be called at + /// any point that the server would like to "fail" the call (though note + /// this shouldn't be called concurrently with any other "sending" call, like + /// \a AsyncWriterInterface::Write). + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), and status, or if some failure occurred + /// when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once FinishWithError returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + /// - Note: \a status must have a non-OK code. + virtual void FinishWithError(const ::grpc::Status& status, void* tag) = 0; +}; + +/// Async server-side API for doing client-streaming RPCs, +/// where the incoming message stream from the client has messages of type \a R, +/// and the single response message sent from the server is type \a W. +template +class ServerAsyncReader final : public ServerAsyncReaderInterface { + public: + explicit ServerAsyncReader(::grpc_impl::ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Implicit input parameter: + /// - The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + 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; + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) override { + read_ops_.set_output_tag(tag); + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + /// See the \a ServerAsyncReaderInterface.Read method for semantics + /// + /// Side effect: + /// - also sends initial metadata if not alreay sent. + /// - uses the \a ServerContext associated with this call to send possible + /// initial and trailing metadata. + /// + /// Note: \a msg is not sent if \a status has a non-OK code. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to deallocate once Finish returns. + void Finish(const W& msg, const ::grpc::Status& status, void* tag) override { + finish_ops_.set_output_tag(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 (status.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessage(msg)); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + } + call_.PerformOps(&finish_ops_); + } + + /// See the \a ServerAsyncReaderInterface.Read method for semantics + /// + /// Side effect: + /// - also sends initial metadata if not alreay sent. + /// - uses the \a ServerContext associated with this call to send possible + /// initial and trailing metadata. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once FinishWithError returns. + void FinishWithError(const ::grpc::Status& status, void* tag) override { + GPR_CODEGEN_ASSERT(!status.ok()); + finish_ops_.set_output_tag(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_, status); + call_.PerformOps(&finish_ops_); + } + + private: + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + ::grpc::internal::Call call_; + ::grpc_impl::ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; +}; + +template +class ServerAsyncWriterInterface + : public ::grpc::internal::ServerAsyncStreamingInterface, + public internal::AsyncWriterInterface { + public: + /// Indicate that the stream is to be finished with a certain status code. + /// Request notification for when the server has sent the appropriate + /// signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when either: + /// * all messages from the client have been received (either known + /// implictly, or explicitly because a previous \a + /// AsyncReaderInterface::Read operation with a non-ok + /// result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'. + /// * it is desired to end the call early with some non-OK status code. + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), response message, and status, or if + /// some failure occurred when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + virtual void Finish(const ::grpc::Status& status, void* tag) = 0; + + /// Request the writing of \a msg and coalesce it with trailing metadata which + /// contains \a status, using WriteOptions options with + /// identifying tag \a tag. + /// + /// WriteAndFinish is equivalent of performing WriteLast and Finish + /// in a single step. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to deallocate once WriteAndFinish returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] status The Status that server returns to client. + /// \param[in] tag The tag identifying the operation. + virtual void WriteAndFinish(const W& msg, ::grpc::WriteOptions options, + const ::grpc::Status& status, void* tag) = 0; +}; + +/// Async server-side API for doing server streaming RPCs, +/// where the outgoing message stream from the server has messages of type \a W. +template +class ServerAsyncWriter final : public ServerAsyncWriterInterface { + public: + explicit ServerAsyncWriter(::grpc_impl::ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Implicit input parameter: + /// - The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + /// + /// \param[in] tag Tag identifying this request. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + 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; + call_.PerformOps(&meta_ops_); + } + + void Write(const W& msg, void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, ::grpc::WriteOptions options, void* tag) override { + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + + EnsureInitialMetadataSent(&write_ops_); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used + /// for sending trailing (and initial) metadata to the client. + /// + /// Note: \a status must have an OK code. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to deallocate once WriteAndFinish returns. + void WriteAndFinish(const W& msg, ::grpc::WriteOptions options, + const ::grpc::Status& status, void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + options.set_buffer_hint(); + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncWriterInterface.Finish method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used for sending + /// trailing (and initial if not already sent) metadata to the client. + /// + /// Note: there are no restrictions are the code of + /// \a status,it may be non-OK + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + void Finish(const ::grpc::Status& status, void* tag) override { + finish_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&finish_ops_); + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + template + void EnsureInitialMetadataSent(T* ops) { + if (!ctx_->sent_initial_metadata_) { + ops->SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops->set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + } + + ::grpc::internal::Call call_; + ::grpc_impl::ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; +}; + +/// Server-side interface for asynchronous bi-directional streaming. +template +class ServerAsyncReaderWriterInterface + : public ::grpc::internal::ServerAsyncStreamingInterface, + public internal::AsyncWriterInterface, + public internal::AsyncReaderInterface { + public: + /// Indicate that the stream is to be finished with a certain status code. + /// Request notification for when the server has sent the appropriate + /// signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when either: + /// * all messages from the client have been received (either known + /// implictly, or explicitly because a previous \a + /// AsyncReaderInterface::Read operation + /// with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' + /// with 'false'. + /// * it is desired to end the call early with some non-OK status code. + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), response message, and status, or if some + /// failure occurred when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + virtual void Finish(const ::grpc::Status& status, void* tag) = 0; + + /// Request the writing of \a msg and coalesce it with trailing metadata which + /// contains \a status, using WriteOptions options with + /// identifying tag \a tag. + /// + /// WriteAndFinish is equivalent of performing WriteLast and Finish in a + /// single step. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to deallocate once WriteAndFinish returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] status The Status that server returns to client. + /// \param[in] tag The tag identifying the operation. + virtual void WriteAndFinish(const W& msg, ::grpc::WriteOptions options, + const ::grpc::Status& status, void* tag) = 0; +}; + +/// Async server-side API for doing bidirectional streaming RPCs, +/// where the incoming message stream coming from the client has messages of +/// type \a R, and the outgoing message stream coming from the server has +/// messages of type \a W. +template +class ServerAsyncReaderWriter final + : public ServerAsyncReaderWriterInterface { + public: + explicit ServerAsyncReaderWriter(::grpc_impl::ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Implicit input parameter: + /// - The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + /// + /// \param[in] tag Tag identifying this request. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + 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; + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) override { + read_ops_.set_output_tag(tag); + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Write(const W& msg, void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, ::grpc::WriteOptions options, void* tag) override { + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + EnsureInitialMetadataSent(&write_ops_); + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish + /// method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used + /// for sending trailing (and initial) metadata to the client. + /// + /// Note: \a status must have an OK code. + // + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to deallocate once WriteAndFinish returns. + void WriteAndFinish(const W& msg, ::grpc::WriteOptions options, + const ::grpc::Status& status, void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + options.set_buffer_hint(); + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used for sending + /// trailing (and initial if not already sent) metadata to the client. + /// + /// Note: there are no restrictions are the code of \a status, + /// it may be non-OK + // + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + void Finish(const ::grpc::Status& status, void* tag) override { + finish_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&finish_ops_); + + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + friend class ::grpc_impl::Server; + + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + template + void EnsureInitialMetadataSent(T* ops) { + if (!ctx_->sent_initial_metadata_) { + ops->SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops->set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + } + + ::grpc::internal::Call call_; + ::grpc_impl::ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; +}; + +} // namespace grpc_impl +#endif // GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_IMPL_H diff --git a/include/grpcpp/impl/codegen/async_unary_call.h b/include/grpcpp/impl/codegen/async_unary_call.h index 5da6a649f14..cbbe70172c8 100644 --- a/include/grpcpp/impl/codegen/async_unary_call.h +++ b/include/grpcpp/impl/codegen/async_unary_call.h @@ -19,299 +19,28 @@ #ifndef GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H #define GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H -#include -#include -#include -#include -#include -#include -#include +#include namespace grpc { -extern CoreCodegenInterface* g_core_codegen_interface; - -/// An interface relevant for async client side unary RPCs (which send -/// one request message to a server and receive one response message). -template -class ClientAsyncResponseReaderInterface { - public: - virtual ~ClientAsyncResponseReaderInterface() {} - - /// Start the call that was set up by the constructor, but only if the - /// constructor was invoked through the "Prepare" API which doesn't actually - /// start the call - virtual void StartCall() = 0; - - /// Request notification of the reading of initial metadata. Completion - /// will be notified by \a tag on the associated completion queue. - /// This call is optional, but if it is used, it cannot be used concurrently - /// with or after the \a Finish method. - /// - /// \param[in] tag Tag identifying this request. - virtual void ReadInitialMetadata(void* tag) = 0; - - /// Request to receive the server's response \a msg and final \a status for - /// the call, and to notify \a tag on this call's completion queue when - /// finished. - /// - /// This function will return when either: - /// - when the server's response message and status have been received. - /// - when the server has returned a non-OK status (no message expected in - /// this case). - /// - when the call failed for some reason and the library generated a - /// non-OK status. - /// - /// \param[in] tag Tag identifying this request. - /// \param[out] status To be updated with the operation status. - /// \param[out] msg To be filled in with the server's response message. - virtual void Finish(R* msg, Status* status, void* tag) = 0; -}; - -namespace internal { template -class ClientAsyncResponseReaderFactory { - public: - /// Start a call and write the request out if \a start is set. - /// \a tag will be notified on \a cq when the call has been started (i.e. - /// intitial metadata sent) and \a request has been written out. - /// If \a start is not set, the actual call must be initiated by StartCall - /// Note that \a context will be used to fill in custom initial metadata - /// used to send to the server when starting the call. - template - static ClientAsyncResponseReader* Create( - ChannelInterface* channel, ::grpc_impl::CompletionQueue* cq, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, const W& request, bool start) { - ::grpc::internal::Call call = channel->CreateCall(method, context, cq); - return new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientAsyncResponseReader))) - ClientAsyncResponseReader(call, context, request, start); - } -}; -} // namespace internal +using ClientAsyncResponseReaderInterface = + grpc_impl::ClientAsyncResponseReaderInterface; -/// Async API for client-side unary RPCs, where the message response -/// received from the server is of type \a R. template -class ClientAsyncResponseReader final - : public ClientAsyncResponseReaderInterface { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientAsyncResponseReader)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void StartCall() override { - assert(!started_); - started_ = true; - StartCallInternal(); - } - - /// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for - /// semantics. - /// - /// Side effect: - /// - the \a ClientContext associated with this call is updated with - /// possible initial and trailing metadata sent from the server. - void ReadInitialMetadata(void* tag) override { - assert(started_); - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - single_buf.set_output_tag(tag); - single_buf.RecvInitialMetadata(context_); - call_.PerformOps(&single_buf); - initial_metadata_read_ = true; - } - - /// See \a ClientAysncResponseReaderInterface::Finish for semantics. - /// - /// Side effect: - /// - the \a ClientContext associated with this call is updated with - /// possible initial and trailing metadata sent from the server. - void Finish(R* msg, Status* status, void* tag) override { - assert(started_); - if (initial_metadata_read_) { - finish_buf.set_output_tag(tag); - finish_buf.RecvMessage(msg); - finish_buf.AllowNoMessage(); - finish_buf.ClientRecvStatus(context_, status); - call_.PerformOps(&finish_buf); - } else { - single_buf.set_output_tag(tag); - single_buf.RecvInitialMetadata(context_); - single_buf.RecvMessage(msg); - single_buf.AllowNoMessage(); - single_buf.ClientRecvStatus(context_, status); - call_.PerformOps(&single_buf); - } - } - - private: - friend class internal::ClientAsyncResponseReaderFactory; - ::grpc_impl::ClientContext* const context_; - ::grpc::internal::Call call_; - bool started_; - bool initial_metadata_read_ = false; +using ClientAsyncResponseReader = grpc_impl::ClientAsyncResponseReader; - template - ClientAsyncResponseReader(::grpc::internal::Call call, - ::grpc_impl::ClientContext* context, - const W& request, bool start) - : context_(context), call_(call), started_(start) { - // Bind the metadata at time of StartCallInternal but set up the rest here - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(single_buf.SendMessage(request).ok()); - single_buf.ClientSendClose(); - if (start) StartCallInternal(); - } - - void StartCallInternal() { - single_buf.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - } - - // disable operator new - static void* operator new(std::size_t size); - static void* operator new(std::size_t size, void* p) { return p; } - - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose, - ::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpRecvMessage, - ::grpc::internal::CallOpClientRecvStatus> - single_buf; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage, - ::grpc::internal::CallOpClientRecvStatus> - finish_buf; -}; - -/// Async server-side API for handling unary calls, where the single -/// response message sent to the client is of type \a W. template -class ServerAsyncResponseWriter final - : public internal::ServerAsyncStreamingInterface { - public: - explicit ServerAsyncResponseWriter(::grpc_impl::ServerContext* ctx) - : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} - - /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. - /// - /// Side effect: - /// The initial metadata that will be sent to the client from this op will - /// be taken from the \a ServerContext associated with the call. - /// - /// \param[in] tag Tag identifying this request. - void SendInitialMetadata(void* tag) override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - meta_buf_.set_output_tag(tag); - meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_buf_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - call_.PerformOps(&meta_buf_); - } - - /// Indicate that the stream is to be finished and request notification - /// when the server has sent the appropriate signals to the client to - /// end the call. Should not be used concurrently with other operations. - /// - /// \param[in] tag Tag identifying this request. - /// \param[in] status To be sent to the client as the result of the call. - /// \param[in] msg Message to be sent to the client. - /// - /// Side effect: - /// - also sends initial metadata if not already sent (using the - /// \a ServerContext associated with this call). - /// - /// Note: if \a status has a non-OK code, then \a msg will not be sent, - /// and the client will receive only the status with possible trailing - /// metadata. - void Finish(const W& msg, const Status& status, void* tag) { - finish_buf_.set_output_tag(tag); - finish_buf_.set_core_cq_tag(&finish_buf_); - if (!ctx_->sent_initial_metadata_) { - finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_buf_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // The response is dropped if the status is not OK. - if (status.ok()) { - finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, - finish_buf_.SendMessage(msg)); - } else { - finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status); - } - call_.PerformOps(&finish_buf_); - } +using ServerAsyncResponseWriter = ::grpc_impl::ServerAsyncResponseWriter; - /// Indicate that the stream is to be finished with a non-OK status, - /// and request notification for when the server has finished sending the - /// appropriate signals to the client to end the call. - /// Should not be used concurrently with other operations. - /// - /// \param[in] tag Tag identifying this request. - /// \param[in] status To be sent to the client as the result of the call. - /// - Note: \a status must have a non-OK code. - /// - /// Side effect: - /// - also sends initial metadata if not already sent (using the - /// \a ServerContext associated with this call). - void FinishWithError(const Status& status, void* tag) { - GPR_CODEGEN_ASSERT(!status.ok()); - finish_buf_.set_output_tag(tag); - if (!ctx_->sent_initial_metadata_) { - finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_buf_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status); - call_.PerformOps(&finish_buf_); - } +namespace internal { - private: - void BindCall(::grpc::internal::Call* call) override { call_ = *call; } +template +using ClientAsyncResponseReaderFactory = + ::grpc_impl::internal::ClientAsyncResponseReaderFactory; - ::grpc::internal::Call call_; - ::grpc_impl::ServerContext* ctx_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> - meta_buf_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpServerSendStatus> - finish_buf_; -}; +} // namespace internal } // namespace grpc -namespace std { -template -class default_delete> { - public: - void operator()(void* p) {} -}; -template -class default_delete> { - public: - void operator()(void* p) {} -}; -} // namespace std - #endif // GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H diff --git a/include/grpcpp/impl/codegen/async_unary_call_impl.h b/include/grpcpp/impl/codegen/async_unary_call_impl.h new file mode 100644 index 00000000000..ff8bf15602b --- /dev/null +++ b/include/grpcpp/impl/codegen/async_unary_call_impl.h @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015 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_ASYNC_UNARY_CALL_IMPL_H +#define GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_IMPL_H + +#include +#include +#include +#include +#include +#include +#include + +namespace grpc_impl { + +/// An interface relevant for async client side unary RPCs (which send +/// one request message to a server and receive one response message). +template +class ClientAsyncResponseReaderInterface { + public: + virtual ~ClientAsyncResponseReaderInterface() {} + + /// Start the call that was set up by the constructor, but only if the + /// constructor was invoked through the "Prepare" API which doesn't actually + /// start the call + virtual void StartCall() = 0; + + /// Request notification of the reading of initial metadata. Completion + /// will be notified by \a tag on the associated completion queue. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a Finish method. + /// + /// \param[in] tag Tag identifying this request. + virtual void ReadInitialMetadata(void* tag) = 0; + + /// Request to receive the server's response \a msg and final \a status for + /// the call, and to notify \a tag on this call's completion queue when + /// finished. + /// + /// This function will return when either: + /// - when the server's response message and status have been received. + /// - when the server has returned a non-OK status (no message expected in + /// this case). + /// - when the call failed for some reason and the library generated a + /// non-OK status. + /// + /// \param[in] tag Tag identifying this request. + /// \param[out] status To be updated with the operation status. + /// \param[out] msg To be filled in with the server's response message. + virtual void Finish(R* msg, ::grpc::Status* status, void* tag) = 0; +}; + +namespace internal { +template +class ClientAsyncResponseReaderFactory { + public: + /// Start a call and write the request out if \a start is set. + /// \a tag will be notified on \a cq when the call has been started (i.e. + /// intitial metadata sent) and \a request has been written out. + /// If \a start is not set, the actual call must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + template + static ClientAsyncResponseReader* Create( + ::grpc::ChannelInterface* channel, ::grpc_impl::CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, const W& request, bool start) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + return new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncResponseReader))) + ClientAsyncResponseReader(call, context, request, start); + } +}; +} // namespace internal + +/// Async API for client-side unary RPCs, where the message response +/// received from the server is of type \a R. +template +class ClientAsyncResponseReader final + : public ClientAsyncResponseReaderInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncResponseReader)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall() override { + assert(!started_); + started_ = true; + StartCallInternal(); + } + + /// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for + /// semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata sent from the server. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + single_buf.set_output_tag(tag); + single_buf.RecvInitialMetadata(context_); + call_.PerformOps(&single_buf); + initial_metadata_read_ = true; + } + + /// See \a ClientAysncResponseReaderInterface::Finish for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata sent from the server. + void Finish(R* msg, ::grpc::Status* status, void* tag) override { + assert(started_); + if (initial_metadata_read_) { + finish_buf.set_output_tag(tag); + finish_buf.RecvMessage(msg); + finish_buf.AllowNoMessage(); + finish_buf.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_buf); + } else { + single_buf.set_output_tag(tag); + single_buf.RecvInitialMetadata(context_); + single_buf.RecvMessage(msg); + single_buf.AllowNoMessage(); + single_buf.ClientRecvStatus(context_, status); + call_.PerformOps(&single_buf); + } + } + + private: + friend class internal::ClientAsyncResponseReaderFactory; + ::grpc_impl::ClientContext* const context_; + ::grpc::internal::Call call_; + bool started_; + bool initial_metadata_read_ = false; + + template + ClientAsyncResponseReader(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, + const W& request, bool start) + : context_(context), call_(call), started_(start) { + // Bind the metadata at time of StartCallInternal but set up the rest here + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(single_buf.SendMessage(request).ok()); + single_buf.ClientSendClose(); + if (start) StartCallInternal(); + } + + void StartCallInternal() { + single_buf.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + } + + // disable operator new + static void* operator new(std::size_t size); + static void* operator new(std::size_t size, void* p) { return p; } + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose, + ::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + single_buf; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + finish_buf; +}; + +/// Async server-side API for handling unary calls, where the single +/// response message sent to the client is of type \a W. +template +class ServerAsyncResponseWriter final + : public ::grpc::internal::ServerAsyncStreamingInterface { + public: + explicit ServerAsyncResponseWriter(::grpc_impl::ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Side effect: + /// The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + /// + /// \param[in] tag Tag identifying this request. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_buf_.set_output_tag(tag); + meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_buf_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_buf_); + } + + /// Indicate that the stream is to be finished and request notification + /// when the server has sent the appropriate signals to the client to + /// end the call. Should not be used concurrently with other operations. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of the call. + /// \param[in] msg Message to be sent to the client. + /// + /// Side effect: + /// - also sends initial metadata if not already sent (using the + /// \a ServerContext associated with this call). + /// + /// Note: if \a status has a non-OK code, then \a msg will not be sent, + /// and the client will receive only the status with possible trailing + /// metadata. + void Finish(const W& msg, const ::grpc::Status& status, void* tag) { + finish_buf_.set_output_tag(tag); + finish_buf_.set_core_cq_tag(&finish_buf_); + if (!ctx_->sent_initial_metadata_) { + finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_buf_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (status.ok()) { + finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_buf_.SendMessage(msg)); + } else { + finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status); + } + call_.PerformOps(&finish_buf_); + } + + /// Indicate that the stream is to be finished with a non-OK status, + /// and request notification for when the server has finished sending the + /// appropriate signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of the call. + /// - Note: \a status must have a non-OK code. + /// + /// Side effect: + /// - also sends initial metadata if not already sent (using the + /// \a ServerContext associated with this call). + void FinishWithError(const ::grpc::Status& status, void* tag) { + GPR_CODEGEN_ASSERT(!status.ok()); + finish_buf_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_buf_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_buf_); + } + + private: + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + ::grpc::internal::Call call_; + ::grpc_impl::ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_buf_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_buf_; +}; + +} // namespace grpc_impl + +namespace std { +template +class default_delete<::grpc_impl::ClientAsyncResponseReader> { + public: + void operator()(void* p) {} +}; +template +class default_delete<::grpc_impl::ClientAsyncResponseReaderInterface> { + public: + void operator()(void* p) {} +}; +} // namespace std + +#endif // GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_IMPL_H diff --git a/include/grpcpp/impl/codegen/byte_buffer.h b/include/grpcpp/impl/codegen/byte_buffer.h index e2ba9344bcb..0b7be6fc753 100644 --- a/include/grpcpp/impl/codegen/byte_buffer.h +++ b/include/grpcpp/impl/codegen/byte_buffer.h @@ -29,6 +29,17 @@ #include +namespace grpc_impl { +namespace internal { + +template +class CallbackUnaryHandler; +template +class CallbackServerStreamingHandler; + +} // namespace internal +} // namespace grpc_impl + namespace grpc { class ServerInterface; @@ -45,10 +56,6 @@ template class RpcMethodHandler; template class ServerStreamingHandler; -template -class CallbackUnaryHandler; -template -class CallbackServerStreamingHandler; template class ErrorMethodHandler; class ExternalConnectionAcceptorImpl; @@ -176,9 +183,9 @@ class ByteBuffer final { template friend class internal::ServerStreamingHandler; template - friend class internal::CallbackUnaryHandler; + friend class ::grpc_impl::internal::CallbackUnaryHandler; template - friend class ::grpc::internal::CallbackServerStreamingHandler; + friend class ::grpc_impl::internal::CallbackServerStreamingHandler; template friend class internal::ErrorMethodHandler; template diff --git a/include/grpcpp/impl/codegen/call_op_set.h b/include/grpcpp/impl/codegen/call_op_set.h index d0958bbb251..84d1407cbe2 100644 --- a/include/grpcpp/impl/codegen/call_op_set.h +++ b/include/grpcpp/impl/codegen/call_op_set.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/grpcpp/impl/codegen/channel_interface.h b/include/grpcpp/impl/codegen/channel_interface.h index 836c287e509..dd2fe8d687a 100644 --- a/include/grpcpp/impl/codegen/channel_interface.h +++ b/include/grpcpp/impl/codegen/channel_interface.h @@ -27,24 +27,13 @@ namespace grpc_impl { class ClientContext; class CompletionQueue; -} // namespace grpc_impl - -namespace grpc { -class ChannelInterface; - template class ClientReader; template class ClientWriter; template class ClientReaderWriter; - namespace internal { -class Call; -class CallOpSetInterface; -class RpcMethod; -template -class BlockingUnaryCallImpl; template class CallbackUnaryCallImpl; template @@ -62,7 +51,19 @@ class ClientCallbackReaderFactory; template class ClientCallbackWriterFactory; class ClientCallbackUnaryFactory; +} // namespace internal +} // namespace grpc_impl + +namespace grpc { +class ChannelInterface; + +namespace internal { +class Call; +class CallOpSetInterface; +class RpcMethod; class InterceptedChannel; +template +class BlockingUnaryCallImpl; } // namespace internal /// Codegen interface for \a grpc::Channel. @@ -102,30 +103,30 @@ class ChannelInterface { private: template - friend class ::grpc::ClientReader; + friend class ::grpc_impl::ClientReader; template - friend class ::grpc::ClientWriter; + friend class ::grpc_impl::ClientWriter; template - friend class ::grpc::ClientReaderWriter; + friend class ::grpc_impl::ClientReaderWriter; template - friend class ::grpc::internal::ClientAsyncReaderFactory; + friend class ::grpc_impl::internal::ClientAsyncReaderFactory; template - friend class ::grpc::internal::ClientAsyncWriterFactory; + friend class ::grpc_impl::internal::ClientAsyncWriterFactory; template - friend class ::grpc::internal::ClientAsyncReaderWriterFactory; + friend class ::grpc_impl::internal::ClientAsyncReaderWriterFactory; template - friend class ::grpc::internal::ClientAsyncResponseReaderFactory; + friend class ::grpc_impl::internal::ClientAsyncResponseReaderFactory; template - friend class ::grpc::internal::ClientCallbackReaderWriterFactory; + friend class ::grpc_impl::internal::ClientCallbackReaderWriterFactory; template - friend class ::grpc::internal::ClientCallbackReaderFactory; + friend class ::grpc_impl::internal::ClientCallbackReaderFactory; template - friend class ::grpc::internal::ClientCallbackWriterFactory; - friend class ::grpc::internal::ClientCallbackUnaryFactory; + friend class ::grpc_impl::internal::ClientCallbackWriterFactory; + friend class ::grpc_impl::internal::ClientCallbackUnaryFactory; template friend class ::grpc::internal::BlockingUnaryCallImpl; template - friend class ::grpc::internal::CallbackUnaryCallImpl; + friend class ::grpc_impl::internal::CallbackUnaryCallImpl; friend class ::grpc::internal::RpcMethod; friend class ::grpc::internal::InterceptedChannel; virtual internal::Call CreateCall(const internal::RpcMethod& method, diff --git a/include/grpcpp/impl/codegen/client_callback.h b/include/grpcpp/impl/codegen/client_callback.h index 9441a48b051..9d183725b57 100644 --- a/include/grpcpp/impl/codegen/client_callback.h +++ b/include/grpcpp/impl/codegen/client_callback.h @@ -19,1012 +19,37 @@ #ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H #define GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace grpc_impl { -class Channel; -class ClientContext; -} // namespace grpc_impl +#include namespace grpc { - -namespace internal { -class RpcMethod; - -/// Perform a callback-based unary call -/// TODO(vjpai): Combine as much as possible with the blocking unary call code -template -void CallbackUnaryCall(ChannelInterface* channel, const RpcMethod& method, - ::grpc_impl::ClientContext* context, - const InputMessage* request, OutputMessage* result, - std::function on_completion) { - CallbackUnaryCallImpl x( - channel, method, context, request, result, on_completion); -} - -template -class CallbackUnaryCallImpl { - public: - CallbackUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method, - ::grpc_impl::ClientContext* context, - const InputMessage* request, OutputMessage* result, - std::function on_completion) { - CompletionQueue* cq = channel->CallbackCQ(); - GPR_CODEGEN_ASSERT(cq != nullptr); - Call call(channel->CreateCall(method, context, cq)); - - using FullCallOpSet = - CallOpSet, - CallOpClientSendClose, CallOpClientRecvStatus>; - - auto* ops = new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(FullCallOpSet))) FullCallOpSet; - - auto* tag = new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(CallbackWithStatusTag))) - CallbackWithStatusTag(call.call(), on_completion, ops); - - // TODO(vjpai): Unify code with sync API as much as possible - Status s = ops->SendMessagePtr(request); - if (!s.ok()) { - tag->force_run(s); - return; - } - ops->SendInitialMetadata(&context->send_initial_metadata_, - context->initial_metadata_flags()); - ops->RecvInitialMetadata(context); - ops->RecvMessage(result); - ops->AllowNoMessage(); - ops->ClientSendClose(); - ops->ClientRecvStatus(context, tag->status_ptr()); - ops->set_core_cq_tag(tag); - call.PerformOps(ops); - } -}; -} // namespace internal - namespace experimental { -// Forward declarations -template -class ClientBidiReactor; template -class ClientReadReactor; -template -class ClientWriteReactor; -class ClientUnaryReactor; - -// NOTE: The streaming objects are not actually implemented in the public API. -// These interfaces are provided for mocking only. Typical applications -// will interact exclusively with the reactors that they define. -template -class ClientCallbackReaderWriter { - public: - virtual ~ClientCallbackReaderWriter() {} - virtual void StartCall() = 0; - virtual void Write(const Request* req, WriteOptions options) = 0; - virtual void WritesDone() = 0; - virtual void Read(Response* resp) = 0; - virtual void AddHold(int holds) = 0; - virtual void RemoveHold() = 0; - - protected: - void BindReactor(ClientBidiReactor* reactor) { - reactor->BindStream(this); - } -}; - -template -class ClientCallbackReader { - public: - virtual ~ClientCallbackReader() {} - virtual void StartCall() = 0; - virtual void Read(Response* resp) = 0; - virtual void AddHold(int holds) = 0; - virtual void RemoveHold() = 0; - - protected: - void BindReactor(ClientReadReactor* reactor) { - reactor->BindReader(this); - } -}; +using ClientCallbackReader = + ::grpc_impl::experimental::ClientCallbackReader; template -class ClientCallbackWriter { - public: - virtual ~ClientCallbackWriter() {} - virtual void StartCall() = 0; - void Write(const Request* req) { Write(req, WriteOptions()); } - virtual void Write(const Request* req, WriteOptions options) = 0; - void WriteLast(const Request* req, WriteOptions options) { - Write(req, options.set_last_message()); - } - virtual void WritesDone() = 0; +using ClientCallbackWriter = + ::grpc_impl::experimental::ClientCallbackWriter; - virtual void AddHold(int holds) = 0; - virtual void RemoveHold() = 0; - - protected: - void BindReactor(ClientWriteReactor* reactor) { - reactor->BindWriter(this); - } -}; - -class ClientCallbackUnary { - public: - virtual ~ClientCallbackUnary() {} - virtual void StartCall() = 0; - - protected: - void BindReactor(ClientUnaryReactor* reactor); -}; - -// The following classes are the reactor interfaces that are to be implemented -// by the user. They are passed in to the library as an argument to a call on a -// stub (either a codegen-ed call or a generic call). The streaming RPC is -// activated by calling StartCall, possibly after initiating StartRead, -// StartWrite, or AddHold operations on the streaming object. Note that none of -// the classes are pure; all reactions have a default empty reaction so that the -// user class only needs to override those classes that it cares about. -// The reactor must be passed to the stub invocation before any of the below -// operations can be called. - -/// \a ClientBidiReactor is the interface for a bidirectional streaming RPC. template -class ClientBidiReactor { - public: - virtual ~ClientBidiReactor() {} - - /// Activate the RPC and initiate any reads or writes that have been Start'ed - /// before this call. All streaming RPCs issued by the client MUST have - /// StartCall invoked on them (even if they are canceled) as this call is the - /// activation of their lifecycle. - void StartCall() { stream_->StartCall(); } - - /// Initiate a read operation (or post it for later initiation if StartCall - /// has not yet been invoked). - /// - /// \param[out] resp Where to eventually store the read message. Valid when - /// the library calls OnReadDone - void StartRead(Response* resp) { stream_->Read(resp); } - - /// Initiate a write operation (or post it for later initiation if StartCall - /// has not yet been invoked). - /// - /// \param[in] req The message to be written. The library takes temporary - /// ownership until OnWriteDone, at which point the application - /// regains ownership of msg. - void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); } - - /// Initiate/post a write operation with specified options. - /// - /// \param[in] req The message to be written. The library takes temporary - /// ownership until OnWriteDone, at which point the application - /// regains ownership of msg. - /// \param[in] options The WriteOptions to use for writing this message - void StartWrite(const Request* req, WriteOptions options) { - stream_->Write(req, std::move(options)); - } - - /// Initiate/post a write operation with specified options and an indication - /// that this is the last write (like StartWrite and StartWritesDone, merged). - /// Note that calling this means that no more calls to StartWrite, - /// StartWriteLast, or StartWritesDone are allowed. - /// - /// \param[in] req The message to be written. The library takes temporary - /// ownership until OnWriteDone, at which point the application - /// regains ownership of msg. - /// \param[in] options The WriteOptions to use for writing this message - void StartWriteLast(const Request* req, WriteOptions options) { - StartWrite(req, std::move(options.set_last_message())); - } - - /// Indicate that the RPC will have no more write operations. This can only be - /// issued once for a given RPC. This is not required or allowed if - /// StartWriteLast is used since that already has the same implication. - /// Note that calling this means that no more calls to StartWrite, - /// StartWriteLast, or StartWritesDone are allowed. - void StartWritesDone() { stream_->WritesDone(); } - - /// Holds are needed if (and only if) this stream has operations that take - /// place on it after StartCall but from outside one of the reactions - /// (OnReadDone, etc). This is _not_ a common use of the streaming API. - /// - /// Holds must be added before calling StartCall. If a stream still has a hold - /// in place, its resources will not be destroyed even if the status has - /// already come in from the wire and there are currently no active callbacks - /// outstanding. Similarly, the stream will not call OnDone if there are still - /// holds on it. - /// - /// For example, if a StartRead or StartWrite operation is going to be - /// initiated from elsewhere in the application, the application should call - /// AddHold or AddMultipleHolds before StartCall. If there is going to be, - /// for example, a read-flow and a write-flow taking place outside the - /// reactions, then call AddMultipleHolds(2) before StartCall. When the - /// application knows that it won't issue any more read operations (such as - /// when a read comes back as not ok), it should issue a RemoveHold(). It - /// should also call RemoveHold() again after it does StartWriteLast or - /// StartWritesDone that indicates that there will be no more write ops. - /// The number of RemoveHold calls must match the total number of AddHold - /// calls plus the number of holds added by AddMultipleHolds. - void AddHold() { AddMultipleHolds(1); } - void AddMultipleHolds(int holds) { stream_->AddHold(holds); } - void RemoveHold() { stream_->RemoveHold(); } - - /// Notifies the application that all operations associated with this RPC - /// have completed and provides the RPC status outcome. - /// - /// \param[in] s The status outcome of this RPC - virtual void OnDone(const Status& s) {} - - /// Notifies the application that a read of initial metadata from the - /// server is done. If the application chooses not to implement this method, - /// it can assume that the initial metadata has been read before the first - /// call of OnReadDone or OnDone. - /// - /// \param[in] ok Was the initial metadata read successfully? If false, no - /// further read-side operation will succeed. - virtual void OnReadInitialMetadataDone(bool ok) {} - - /// Notifies the application that a StartRead operation completed. - /// - /// \param[in] ok Was it successful? If false, no further read-side operation - /// will succeed. - virtual void OnReadDone(bool ok) {} - - /// Notifies the application that a StartWrite operation completed. - /// - /// \param[in] ok Was it successful? If false, no further write-side operation - /// will succeed. - virtual void OnWriteDone(bool ok) {} - - /// Notifies the application that a StartWritesDone operation completed. Note - /// that this is only used on explicit StartWritesDone operations and not for - /// those that are implicitly invoked as part of a StartWriteLast. - /// - /// \param[in] ok Was it successful? If false, the application will later see - /// the failure reflected as a bad status in OnDone. - virtual void OnWritesDoneDone(bool ok) {} +using ClientCallbackReaderWriter = + ::grpc_impl::experimental::ClientCallbackReaderWriter; - private: - friend class ClientCallbackReaderWriter; - void BindStream(ClientCallbackReaderWriter* stream) { - stream_ = stream; - } - ClientCallbackReaderWriter* stream_; -}; - -/// \a ClientReadReactor is the interface for a server-streaming RPC. -/// All public methods behave as in ClientBidiReactor. template -class ClientReadReactor { - public: - virtual ~ClientReadReactor() {} - - void StartCall() { reader_->StartCall(); } - void StartRead(Response* resp) { reader_->Read(resp); } - - void AddHold() { AddMultipleHolds(1); } - void AddMultipleHolds(int holds) { reader_->AddHold(holds); } - void RemoveHold() { reader_->RemoveHold(); } - - virtual void OnDone(const Status& s) {} - virtual void OnReadInitialMetadataDone(bool ok) {} - virtual void OnReadDone(bool ok) {} - - private: - friend class ClientCallbackReader; - void BindReader(ClientCallbackReader* reader) { reader_ = reader; } - ClientCallbackReader* reader_; -}; +using ClientReadReactor = + ::grpc_impl::experimental::ClientReadReactor; -/// \a ClientWriteReactor is the interface for a client-streaming RPC. -/// All public methods behave as in ClientBidiReactor. template -class ClientWriteReactor { - public: - virtual ~ClientWriteReactor() {} - - void StartCall() { writer_->StartCall(); } - void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); } - void StartWrite(const Request* req, WriteOptions options) { - writer_->Write(req, std::move(options)); - } - void StartWriteLast(const Request* req, WriteOptions options) { - StartWrite(req, std::move(options.set_last_message())); - } - void StartWritesDone() { writer_->WritesDone(); } - - void AddHold() { AddMultipleHolds(1); } - void AddMultipleHolds(int holds) { writer_->AddHold(holds); } - void RemoveHold() { writer_->RemoveHold(); } - - virtual void OnDone(const Status& s) {} - virtual void OnReadInitialMetadataDone(bool ok) {} - virtual void OnWriteDone(bool ok) {} - virtual void OnWritesDoneDone(bool ok) {} - - private: - friend class ClientCallbackWriter; - void BindWriter(ClientCallbackWriter* writer) { writer_ = writer; } - ClientCallbackWriter* writer_; -}; - -/// \a ClientUnaryReactor is a reactor-style interface for a unary RPC. -/// This is _not_ a common way of invoking a unary RPC. In practice, this -/// option should be used only if the unary RPC wants to receive initial -/// metadata without waiting for the response to complete. Most deployments of -/// RPC systems do not use this option, but it is needed for generality. -/// All public methods behave as in ClientBidiReactor. -/// StartCall is included for consistency with the other reactor flavors: even -/// though there are no StartRead or StartWrite operations to queue before the -/// call (that is part of the unary call itself) and there is no reactor object -/// being created as a result of this call, we keep a consistent 2-phase -/// initiation API among all the reactor flavors. -class ClientUnaryReactor { - public: - virtual ~ClientUnaryReactor() {} - - void StartCall() { call_->StartCall(); } - virtual void OnDone(const Status& s) {} - virtual void OnReadInitialMetadataDone(bool ok) {} - - private: - friend class ClientCallbackUnary; - void BindCall(ClientCallbackUnary* call) { call_ = call; } - ClientCallbackUnary* call_; -}; - -// Define function out-of-line from class to avoid forward declaration issue -inline void ClientCallbackUnary::BindReactor(ClientUnaryReactor* reactor) { - reactor->BindCall(this); -} - -} // namespace experimental - -namespace internal { - -// Forward declare factory classes for friendship -template -class ClientCallbackReaderWriterFactory; -template -class ClientCallbackReaderFactory; -template -class ClientCallbackWriterFactory; - -template -class ClientCallbackReaderWriterImpl - : public ::grpc::experimental::ClientCallbackReaderWriter { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientCallbackReaderWriterImpl)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void MaybeFinish() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - Status s = std::move(finish_status_); - auto* reactor = reactor_; - auto* call = call_.call(); - this->~ClientCallbackReaderWriterImpl(); - g_core_codegen_interface->grpc_call_unref(call); - reactor->OnDone(s); - } - } - - void StartCall() override { - // This call initiates two batches, plus any backlog, each with a callback - // 1. Send initial metadata (unless corked) + recv initial metadata - // 2. Any read backlog - // 3. Any write backlog - // 4. Recv trailing metadata, on_completion callback - started_ = true; - - start_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadInitialMetadataDone(ok); - MaybeFinish(); - }, - &start_ops_); - if (!start_corked_) { - start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - } - start_ops_.RecvInitialMetadata(context_); - start_ops_.set_core_cq_tag(&start_tag_); - call_.PerformOps(&start_ops_); - - // Also set up the read and write tags so that they don't have to be set up - // each time - write_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWriteDone(ok); - MaybeFinish(); - }, - &write_ops_); - write_ops_.set_core_cq_tag(&write_tag_); - - read_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadDone(ok); - MaybeFinish(); - }, - &read_ops_); - read_ops_.set_core_cq_tag(&read_tag_); - if (read_ops_at_start_) { - call_.PerformOps(&read_ops_); - } - - if (write_ops_at_start_) { - call_.PerformOps(&write_ops_); - } - - if (writes_done_ops_at_start_) { - call_.PerformOps(&writes_done_ops_); - } - - finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, - &finish_ops_); - finish_ops_.ClientRecvStatus(context_, &finish_status_); - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void Read(Response* msg) override { - read_ops_.RecvMessage(msg); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (started_) { - call_.PerformOps(&read_ops_); - } else { - read_ops_at_start_ = true; - } - } - - void Write(const Request* msg, WriteOptions options) override { - if (start_corked_) { - write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - start_corked_ = false; - } - - if (options.is_last_message()) { - options.set_buffer_hint(); - write_ops_.ClientSendClose(); - } - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(msg, options).ok()); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (started_) { - call_.PerformOps(&write_ops_); - } else { - write_ops_at_start_ = true; - } - } - void WritesDone() override { - if (start_corked_) { - writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - start_corked_ = false; - } - writes_done_ops_.ClientSendClose(); - writes_done_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWritesDoneDone(ok); - MaybeFinish(); - }, - &writes_done_ops_); - writes_done_ops_.set_core_cq_tag(&writes_done_tag_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (started_) { - call_.PerformOps(&writes_done_ops_); - } else { - writes_done_ops_at_start_ = true; - } - } - - void AddHold(int holds) override { - callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed); - } - void RemoveHold() override { MaybeFinish(); } - - private: - friend class ClientCallbackReaderWriterFactory; - - ClientCallbackReaderWriterImpl( - Call call, ::grpc_impl::ClientContext* context, - ::grpc::experimental::ClientBidiReactor* reactor) - : context_(context), - call_(call), - reactor_(reactor), - start_corked_(context_->initial_metadata_corked_) { - this->BindReactor(reactor); - } - - ::grpc_impl::ClientContext* const context_; - Call call_; - ::grpc::experimental::ClientBidiReactor* const reactor_; - - CallOpSet start_ops_; - CallbackWithSuccessTag start_tag_; - bool start_corked_; - - CallOpSet finish_ops_; - CallbackWithSuccessTag finish_tag_; - Status finish_status_; - - CallOpSet - write_ops_; - CallbackWithSuccessTag write_tag_; - bool write_ops_at_start_{false}; - - CallOpSet writes_done_ops_; - CallbackWithSuccessTag writes_done_tag_; - bool writes_done_ops_at_start_{false}; - - CallOpSet> read_ops_; - CallbackWithSuccessTag read_tag_; - bool read_ops_at_start_{false}; - - // Minimum of 2 callbacks to pre-register for start and finish - std::atomic callbacks_outstanding_{2}; - bool started_{false}; -}; +using ClientWriteReactor = + ::grpc_impl::experimental::ClientWriteReactor; template -class ClientCallbackReaderWriterFactory { - public: - static void Create( - ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, - ::grpc::experimental::ClientBidiReactor* reactor) { - Call call = channel->CreateCall(method, context, channel->CallbackCQ()); - - g_core_codegen_interface->grpc_call_ref(call.call()); - new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientCallbackReaderWriterImpl))) - ClientCallbackReaderWriterImpl(call, context, - reactor); - } -}; - -template -class ClientCallbackReaderImpl - : public ::grpc::experimental::ClientCallbackReader { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientCallbackReaderImpl)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void MaybeFinish() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - Status s = std::move(finish_status_); - auto* reactor = reactor_; - auto* call = call_.call(); - this->~ClientCallbackReaderImpl(); - g_core_codegen_interface->grpc_call_unref(call); - reactor->OnDone(s); - } - } - - void StartCall() override { - // This call initiates two batches, plus any backlog, each with a callback - // 1. Send initial metadata (unless corked) + recv initial metadata - // 2. Any backlog - // 3. Recv trailing metadata, on_completion callback - started_ = true; - - start_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadInitialMetadataDone(ok); - MaybeFinish(); - }, - &start_ops_); - start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - start_ops_.RecvInitialMetadata(context_); - start_ops_.set_core_cq_tag(&start_tag_); - call_.PerformOps(&start_ops_); - - // Also set up the read tag so it doesn't have to be set up each time - read_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadDone(ok); - MaybeFinish(); - }, - &read_ops_); - read_ops_.set_core_cq_tag(&read_tag_); - if (read_ops_at_start_) { - call_.PerformOps(&read_ops_); - } - - finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, - &finish_ops_); - finish_ops_.ClientRecvStatus(context_, &finish_status_); - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void Read(Response* msg) override { - read_ops_.RecvMessage(msg); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (started_) { - call_.PerformOps(&read_ops_); - } else { - read_ops_at_start_ = true; - } - } - - void AddHold(int holds) override { - callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed); - } - void RemoveHold() override { MaybeFinish(); } - - private: - friend class ClientCallbackReaderFactory; - - template - ClientCallbackReaderImpl( - Call call, ::grpc_impl::ClientContext* context, Request* request, - ::grpc::experimental::ClientReadReactor* reactor) - : context_(context), call_(call), reactor_(reactor) { - this->BindReactor(reactor); - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(start_ops_.SendMessagePtr(request).ok()); - start_ops_.ClientSendClose(); - } - - ::grpc_impl::ClientContext* const context_; - Call call_; - ::grpc::experimental::ClientReadReactor* const reactor_; - - CallOpSet - start_ops_; - CallbackWithSuccessTag start_tag_; - - CallOpSet finish_ops_; - CallbackWithSuccessTag finish_tag_; - Status finish_status_; - - CallOpSet> read_ops_; - CallbackWithSuccessTag read_tag_; - bool read_ops_at_start_{false}; - - // Minimum of 2 callbacks to pre-register for start and finish - std::atomic callbacks_outstanding_{2}; - bool started_{false}; -}; - -template -class ClientCallbackReaderFactory { - public: - template - static void Create( - ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, const Request* request, - ::grpc::experimental::ClientReadReactor* reactor) { - Call call = channel->CreateCall(method, context, channel->CallbackCQ()); - - g_core_codegen_interface->grpc_call_ref(call.call()); - new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientCallbackReaderImpl))) - ClientCallbackReaderImpl(call, context, request, reactor); - } -}; - -template -class ClientCallbackWriterImpl - : public ::grpc::experimental::ClientCallbackWriter { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientCallbackWriterImpl)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void MaybeFinish() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - Status s = std::move(finish_status_); - auto* reactor = reactor_; - auto* call = call_.call(); - this->~ClientCallbackWriterImpl(); - g_core_codegen_interface->grpc_call_unref(call); - reactor->OnDone(s); - } - } - - void StartCall() override { - // This call initiates two batches, plus any backlog, each with a callback - // 1. Send initial metadata (unless corked) + recv initial metadata - // 2. Any backlog - // 3. Recv trailing metadata, on_completion callback - started_ = true; +using ClientBidiReactor = + ::grpc_impl::experimental::ClientBidiReactor; - start_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadInitialMetadataDone(ok); - MaybeFinish(); - }, - &start_ops_); - if (!start_corked_) { - start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - } - start_ops_.RecvInitialMetadata(context_); - start_ops_.set_core_cq_tag(&start_tag_); - call_.PerformOps(&start_ops_); - - // Also set up the read and write tags so that they don't have to be set up - // each time - write_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWriteDone(ok); - MaybeFinish(); - }, - &write_ops_); - write_ops_.set_core_cq_tag(&write_tag_); - - if (write_ops_at_start_) { - call_.PerformOps(&write_ops_); - } - - if (writes_done_ops_at_start_) { - call_.PerformOps(&writes_done_ops_); - } - - finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, - &finish_ops_); - finish_ops_.ClientRecvStatus(context_, &finish_status_); - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void Write(const Request* msg, WriteOptions options) override { - if (start_corked_) { - write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - start_corked_ = false; - } - - if (options.is_last_message()) { - options.set_buffer_hint(); - write_ops_.ClientSendClose(); - } - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(msg, options).ok()); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (started_) { - call_.PerformOps(&write_ops_); - } else { - write_ops_at_start_ = true; - } - } - void WritesDone() override { - if (start_corked_) { - writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - start_corked_ = false; - } - writes_done_ops_.ClientSendClose(); - writes_done_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWritesDoneDone(ok); - MaybeFinish(); - }, - &writes_done_ops_); - writes_done_ops_.set_core_cq_tag(&writes_done_tag_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (started_) { - call_.PerformOps(&writes_done_ops_); - } else { - writes_done_ops_at_start_ = true; - } - } - - void AddHold(int holds) override { - callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed); - } - void RemoveHold() override { MaybeFinish(); } - - private: - friend class ClientCallbackWriterFactory; - - template - ClientCallbackWriterImpl( - Call call, ::grpc_impl::ClientContext* context, Response* response, - ::grpc::experimental::ClientWriteReactor* reactor) - : context_(context), - call_(call), - reactor_(reactor), - start_corked_(context_->initial_metadata_corked_) { - this->BindReactor(reactor); - finish_ops_.RecvMessage(response); - finish_ops_.AllowNoMessage(); - } - - ::grpc_impl::ClientContext* const context_; - Call call_; - ::grpc::experimental::ClientWriteReactor* const reactor_; - - CallOpSet start_ops_; - CallbackWithSuccessTag start_tag_; - bool start_corked_; - - CallOpSet finish_ops_; - CallbackWithSuccessTag finish_tag_; - Status finish_status_; - - CallOpSet - write_ops_; - CallbackWithSuccessTag write_tag_; - bool write_ops_at_start_{false}; - - CallOpSet writes_done_ops_; - CallbackWithSuccessTag writes_done_tag_; - bool writes_done_ops_at_start_{false}; - - // Minimum of 2 callbacks to pre-register for start and finish - std::atomic callbacks_outstanding_{2}; - bool started_{false}; -}; - -template -class ClientCallbackWriterFactory { - public: - template - static void Create( - ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, Response* response, - ::grpc::experimental::ClientWriteReactor* reactor) { - Call call = channel->CreateCall(method, context, channel->CallbackCQ()); - - g_core_codegen_interface->grpc_call_ref(call.call()); - new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientCallbackWriterImpl))) - ClientCallbackWriterImpl(call, context, response, reactor); - } -}; - -class ClientCallbackUnaryImpl final - : public ::grpc::experimental::ClientCallbackUnary { - public: - // always allocated against a call arena, no memory free required - static void operator delete(void* ptr, std::size_t size) { - assert(size == sizeof(ClientCallbackUnaryImpl)); - } - - // This operator should never be called as the memory should be freed as part - // of the arena destruction. It only exists to provide a matching operator - // delete to the operator new so that some compilers will not complain (see - // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this - // there are no tests catching the compiler warning. - static void operator delete(void*, void*) { assert(0); } - - void StartCall() override { - // This call initiates two batches, each with a callback - // 1. Send initial metadata + write + writes done + recv initial metadata - // 2. Read message, recv trailing metadata - started_ = true; - - start_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadInitialMetadataDone(ok); - MaybeFinish(); - }, - &start_ops_); - start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - start_ops_.RecvInitialMetadata(context_); - start_ops_.set_core_cq_tag(&start_tag_); - call_.PerformOps(&start_ops_); - - finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, - &finish_ops_); - finish_ops_.ClientRecvStatus(context_, &finish_status_); - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void MaybeFinish() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - Status s = std::move(finish_status_); - auto* reactor = reactor_; - auto* call = call_.call(); - this->~ClientCallbackUnaryImpl(); - g_core_codegen_interface->grpc_call_unref(call); - reactor->OnDone(s); - } - } - - private: - friend class ClientCallbackUnaryFactory; - - template - ClientCallbackUnaryImpl(Call call, ::grpc_impl::ClientContext* context, - Request* request, Response* response, - ::grpc::experimental::ClientUnaryReactor* reactor) - : context_(context), call_(call), reactor_(reactor) { - this->BindReactor(reactor); - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(start_ops_.SendMessagePtr(request).ok()); - start_ops_.ClientSendClose(); - finish_ops_.RecvMessage(response); - finish_ops_.AllowNoMessage(); - } - - ::grpc_impl::ClientContext* const context_; - Call call_; - ::grpc::experimental::ClientUnaryReactor* const reactor_; - - CallOpSet - start_ops_; - CallbackWithSuccessTag start_tag_; - - CallOpSet finish_ops_; - CallbackWithSuccessTag finish_tag_; - Status finish_status_; - - // This call will have 2 callbacks: start and finish - std::atomic callbacks_outstanding_{2}; - bool started_{false}; -}; - -class ClientCallbackUnaryFactory { - public: - template - static void Create(ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, - const Request* request, Response* response, - ::grpc::experimental::ClientUnaryReactor* reactor) { - Call call = channel->CreateCall(method, context, channel->CallbackCQ()); - - g_core_codegen_interface->grpc_call_ref(call.call()); - - new (g_core_codegen_interface->grpc_call_arena_alloc( - call.call(), sizeof(ClientCallbackUnaryImpl))) - ClientCallbackUnaryImpl(call, context, request, response, reactor); - } -}; - -} // namespace internal +typedef ::grpc_impl::experimental::ClientUnaryReactor ClientUnaryReactor; +} // namespace experimental } // namespace grpc #endif // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H diff --git a/include/grpcpp/impl/codegen/client_callback_impl.h b/include/grpcpp/impl/codegen/client_callback_impl.h new file mode 100644 index 00000000000..81847bc9e04 --- /dev/null +++ b/include/grpcpp/impl/codegen/client_callback_impl.h @@ -0,0 +1,1067 @@ +/* + * + * 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_CLIENT_CALLBACK_IMPL_H +#define GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_IMPL_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { +namespace internal { +class RpcMethod; +} // namespace internal +} // namespace grpc + +namespace grpc_impl { +class Channel; +class ClientContext; + +namespace internal { + +/// Perform a callback-based unary call +/// TODO(vjpai): Combine as much as possible with the blocking unary call code +template +void CallbackUnaryCall(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + const InputMessage* request, OutputMessage* result, + std::function on_completion) { + CallbackUnaryCallImpl x( + channel, method, context, request, result, on_completion); +} + +template +class CallbackUnaryCallImpl { + public: + CallbackUnaryCallImpl(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + const InputMessage* request, OutputMessage* result, + std::function on_completion) { + ::grpc_impl::CompletionQueue* cq = channel->CallbackCQ(); + GPR_CODEGEN_ASSERT(cq != nullptr); + grpc::internal::Call call(channel->CreateCall(method, context, cq)); + + using FullCallOpSet = grpc::internal::CallOpSet< + ::grpc::internal::CallOpSendInitialMetadata, + grpc::internal::CallOpSendMessage, + grpc::internal::CallOpRecvInitialMetadata, + grpc::internal::CallOpRecvMessage, + grpc::internal::CallOpClientSendClose, + grpc::internal::CallOpClientRecvStatus>; + + auto* ops = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(FullCallOpSet))) FullCallOpSet; + + auto* tag = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(grpc::internal::CallbackWithStatusTag))) + grpc::internal::CallbackWithStatusTag(call.call(), on_completion, ops); + + // TODO(vjpai): Unify code with sync API as much as possible + ::grpc::Status s = ops->SendMessagePtr(request); + if (!s.ok()) { + tag->force_run(s); + return; + } + ops->SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + ops->RecvInitialMetadata(context); + ops->RecvMessage(result); + ops->AllowNoMessage(); + ops->ClientSendClose(); + ops->ClientRecvStatus(context, tag->status_ptr()); + ops->set_core_cq_tag(tag); + call.PerformOps(ops); + } +}; +} // namespace internal + +namespace experimental { + +// Forward declarations +template +class ClientBidiReactor; +template +class ClientReadReactor; +template +class ClientWriteReactor; +class ClientUnaryReactor; + +// NOTE: The streaming objects are not actually implemented in the public API. +// These interfaces are provided for mocking only. Typical applications +// will interact exclusively with the reactors that they define. +template +class ClientCallbackReaderWriter { + public: + virtual ~ClientCallbackReaderWriter() {} + virtual void StartCall() = 0; + virtual void Write(const Request* req, ::grpc::WriteOptions options) = 0; + virtual void WritesDone() = 0; + virtual void Read(Response* resp) = 0; + virtual void AddHold(int holds) = 0; + virtual void RemoveHold() = 0; + + protected: + void BindReactor(ClientBidiReactor* reactor) { + reactor->BindStream(this); + } +}; + +template +class ClientCallbackReader { + public: + virtual ~ClientCallbackReader() {} + virtual void StartCall() = 0; + virtual void Read(Response* resp) = 0; + virtual void AddHold(int holds) = 0; + virtual void RemoveHold() = 0; + + protected: + void BindReactor(ClientReadReactor* reactor) { + reactor->BindReader(this); + } +}; + +template +class ClientCallbackWriter { + public: + virtual ~ClientCallbackWriter() {} + virtual void StartCall() = 0; + void Write(const Request* req) { Write(req, ::grpc::WriteOptions()); } + virtual void Write(const Request* req, ::grpc::WriteOptions options) = 0; + void WriteLast(const Request* req, ::grpc::WriteOptions options) { + Write(req, options.set_last_message()); + } + virtual void WritesDone() = 0; + + virtual void AddHold(int holds) = 0; + virtual void RemoveHold() = 0; + + protected: + void BindReactor(ClientWriteReactor* reactor) { + reactor->BindWriter(this); + } +}; + +class ClientCallbackUnary { + public: + virtual ~ClientCallbackUnary() {} + virtual void StartCall() = 0; + + protected: + void BindReactor(ClientUnaryReactor* reactor); +}; + +// The following classes are the reactor interfaces that are to be implemented +// by the user. They are passed in to the library as an argument to a call on a +// stub (either a codegen-ed call or a generic call). The streaming RPC is +// activated by calling StartCall, possibly after initiating StartRead, +// StartWrite, or AddHold operations on the streaming object. Note that none of +// the classes are pure; all reactions have a default empty reaction so that the +// user class only needs to override those classes that it cares about. +// The reactor must be passed to the stub invocation before any of the below +// operations can be called. + +/// \a ClientBidiReactor is the interface for a bidirectional streaming RPC. +template +class ClientBidiReactor { + public: + virtual ~ClientBidiReactor() {} + + /// Activate the RPC and initiate any reads or writes that have been Start'ed + /// before this call. All streaming RPCs issued by the client MUST have + /// StartCall invoked on them (even if they are canceled) as this call is the + /// activation of their lifecycle. + void StartCall() { stream_->StartCall(); } + + /// Initiate a read operation (or post it for later initiation if StartCall + /// has not yet been invoked). + /// + /// \param[out] resp Where to eventually store the read message. Valid when + /// the library calls OnReadDone + void StartRead(Response* resp) { stream_->Read(resp); } + + /// Initiate a write operation (or post it for later initiation if StartCall + /// has not yet been invoked). + /// + /// \param[in] req The message to be written. The library takes temporary + /// ownership until OnWriteDone, at which point the application + /// regains ownership of msg. + void StartWrite(const Request* req) { + StartWrite(req, ::grpc::WriteOptions()); + } + + /// Initiate/post a write operation with specified options. + /// + /// \param[in] req The message to be written. The library takes temporary + /// ownership until OnWriteDone, at which point the application + /// regains ownership of msg. + /// \param[in] options The WriteOptions to use for writing this message + void StartWrite(const Request* req, ::grpc::WriteOptions options) { + stream_->Write(req, std::move(options)); + } + + /// Initiate/post a write operation with specified options and an indication + /// that this is the last write (like StartWrite and StartWritesDone, merged). + /// Note that calling this means that no more calls to StartWrite, + /// StartWriteLast, or StartWritesDone are allowed. + /// + /// \param[in] req The message to be written. The library takes temporary + /// ownership until OnWriteDone, at which point the application + /// regains ownership of msg. + /// \param[in] options The WriteOptions to use for writing this message + void StartWriteLast(const Request* req, ::grpc::WriteOptions options) { + StartWrite(req, std::move(options.set_last_message())); + } + + /// Indicate that the RPC will have no more write operations. This can only be + /// issued once for a given RPC. This is not required or allowed if + /// StartWriteLast is used since that already has the same implication. + /// Note that calling this means that no more calls to StartWrite, + /// StartWriteLast, or StartWritesDone are allowed. + void StartWritesDone() { stream_->WritesDone(); } + + /// Holds are needed if (and only if) this stream has operations that take + /// place on it after StartCall but from outside one of the reactions + /// (OnReadDone, etc). This is _not_ a common use of the streaming API. + /// + /// Holds must be added before calling StartCall. If a stream still has a hold + /// in place, its resources will not be destroyed even if the status has + /// already come in from the wire and there are currently no active callbacks + /// outstanding. Similarly, the stream will not call OnDone if there are still + /// holds on it. + /// + /// For example, if a StartRead or StartWrite operation is going to be + /// initiated from elsewhere in the application, the application should call + /// AddHold or AddMultipleHolds before StartCall. If there is going to be, + /// for example, a read-flow and a write-flow taking place outside the + /// reactions, then call AddMultipleHolds(2) before StartCall. When the + /// application knows that it won't issue any more read operations (such as + /// when a read comes back as not ok), it should issue a RemoveHold(). It + /// should also call RemoveHold() again after it does StartWriteLast or + /// StartWritesDone that indicates that there will be no more write ops. + /// The number of RemoveHold calls must match the total number of AddHold + /// calls plus the number of holds added by AddMultipleHolds. + void AddHold() { AddMultipleHolds(1); } + void AddMultipleHolds(int holds) { stream_->AddHold(holds); } + void RemoveHold() { stream_->RemoveHold(); } + + /// Notifies the application that all operations associated with this RPC + /// have completed and provides the RPC status outcome. + /// + /// \param[in] s The status outcome of this RPC + virtual void OnDone(const ::grpc::Status& s) {} + + /// Notifies the application that a read of initial metadata from the + /// server is done. If the application chooses not to implement this method, + /// it can assume that the initial metadata has been read before the first + /// call of OnReadDone or OnDone. + /// + /// \param[in] ok Was the initial metadata read successfully? If false, no + /// further read-side operation will succeed. + virtual void OnReadInitialMetadataDone(bool ok) {} + + /// Notifies the application that a StartRead operation completed. + /// + /// \param[in] ok Was it successful? If false, no further read-side operation + /// will succeed. + virtual void OnReadDone(bool ok) {} + + /// Notifies the application that a StartWrite operation completed. + /// + /// \param[in] ok Was it successful? If false, no further write-side operation + /// will succeed. + virtual void OnWriteDone(bool ok) {} + + /// Notifies the application that a StartWritesDone operation completed. Note + /// that this is only used on explicit StartWritesDone operations and not for + /// those that are implicitly invoked as part of a StartWriteLast. + /// + /// \param[in] ok Was it successful? If false, the application will later see + /// the failure reflected as a bad status in OnDone. + virtual void OnWritesDoneDone(bool ok) {} + + private: + friend class ClientCallbackReaderWriter; + void BindStream(ClientCallbackReaderWriter* stream) { + stream_ = stream; + } + ClientCallbackReaderWriter* stream_; +}; + +/// \a ClientReadReactor is the interface for a server-streaming RPC. +/// All public methods behave as in ClientBidiReactor. +template +class ClientReadReactor { + public: + virtual ~ClientReadReactor() {} + + void StartCall() { reader_->StartCall(); } + void StartRead(Response* resp) { reader_->Read(resp); } + + void AddHold() { AddMultipleHolds(1); } + void AddMultipleHolds(int holds) { reader_->AddHold(holds); } + void RemoveHold() { reader_->RemoveHold(); } + + virtual void OnDone(const ::grpc::Status& s) {} + virtual void OnReadInitialMetadataDone(bool ok) {} + virtual void OnReadDone(bool ok) {} + + private: + friend class ClientCallbackReader; + void BindReader(ClientCallbackReader* reader) { reader_ = reader; } + ClientCallbackReader* reader_; +}; + +/// \a ClientWriteReactor is the interface for a client-streaming RPC. +/// All public methods behave as in ClientBidiReactor. +template +class ClientWriteReactor { + public: + virtual ~ClientWriteReactor() {} + + void StartCall() { writer_->StartCall(); } + void StartWrite(const Request* req) { + StartWrite(req, ::grpc::WriteOptions()); + } + void StartWrite(const Request* req, ::grpc::WriteOptions options) { + writer_->Write(req, std::move(options)); + } + void StartWriteLast(const Request* req, ::grpc::WriteOptions options) { + StartWrite(req, std::move(options.set_last_message())); + } + void StartWritesDone() { writer_->WritesDone(); } + + void AddHold() { AddMultipleHolds(1); } + void AddMultipleHolds(int holds) { writer_->AddHold(holds); } + void RemoveHold() { writer_->RemoveHold(); } + + virtual void OnDone(const ::grpc::Status& s) {} + virtual void OnReadInitialMetadataDone(bool ok) {} + virtual void OnWriteDone(bool ok) {} + virtual void OnWritesDoneDone(bool ok) {} + + private: + friend class ClientCallbackWriter; + void BindWriter(ClientCallbackWriter* writer) { writer_ = writer; } + ClientCallbackWriter* writer_; +}; + +/// \a ClientUnaryReactor is a reactor-style interface for a unary RPC. +/// This is _not_ a common way of invoking a unary RPC. In practice, this +/// option should be used only if the unary RPC wants to receive initial +/// metadata without waiting for the response to complete. Most deployments of +/// RPC systems do not use this option, but it is needed for generality. +/// All public methods behave as in ClientBidiReactor. +/// StartCall is included for consistency with the other reactor flavors: even +/// though there are no StartRead or StartWrite operations to queue before the +/// call (that is part of the unary call itself) and there is no reactor object +/// being created as a result of this call, we keep a consistent 2-phase +/// initiation API among all the reactor flavors. +class ClientUnaryReactor { + public: + virtual ~ClientUnaryReactor() {} + + void StartCall() { call_->StartCall(); } + virtual void OnDone(const ::grpc::Status& s) {} + virtual void OnReadInitialMetadataDone(bool ok) {} + + private: + friend class ClientCallbackUnary; + void BindCall(ClientCallbackUnary* call) { call_ = call; } + ClientCallbackUnary* call_; +}; + +// Define function out-of-line from class to avoid forward declaration issue +inline void ClientCallbackUnary::BindReactor(ClientUnaryReactor* reactor) { + reactor->BindCall(this); +} + +} // namespace experimental + +namespace internal { + +// Forward declare factory classes for friendship +template +class ClientCallbackReaderWriterFactory; +template +class ClientCallbackReaderFactory; +template +class ClientCallbackWriterFactory; + +template +class ClientCallbackReaderWriterImpl + : public experimental::ClientCallbackReaderWriter { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackReaderWriterImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void MaybeFinish() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + ::grpc::Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackReaderWriterImpl(); + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + void StartCall() override { + // This call initiates two batches, plus any backlog, each with a callback + // 1. Send initial metadata (unless corked) + recv initial metadata + // 2. Any read backlog + // 3. Any write backlog + // 4. Recv trailing metadata, on_completion callback + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + if (!start_corked_) { + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + } + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + // Also set up the read and write tags so that they don't have to be set up + // each time + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeFinish(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeFinish(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + if (read_ops_at_start_) { + call_.PerformOps(&read_ops_); + } + + if (write_ops_at_start_) { + call_.PerformOps(&write_ops_); + } + + if (writes_done_ops_at_start_) { + call_.PerformOps(&writes_done_ops_); + } + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void Read(Response* msg) override { + read_ops_.RecvMessage(msg); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (started_) { + call_.PerformOps(&read_ops_); + } else { + read_ops_at_start_ = true; + } + } + + void Write(const Request* msg, ::grpc::WriteOptions options) override { + if (start_corked_) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(msg, options).ok()); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (started_) { + call_.PerformOps(&write_ops_); + } else { + write_ops_at_start_ = true; + } + } + void WritesDone() override { + if (start_corked_) { + writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + writes_done_ops_.ClientSendClose(); + writes_done_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWritesDoneDone(ok); + MaybeFinish(); + }, + &writes_done_ops_); + writes_done_ops_.set_core_cq_tag(&writes_done_tag_); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (started_) { + call_.PerformOps(&writes_done_ops_); + } else { + writes_done_ops_at_start_ = true; + } + } + + void AddHold(int holds) override { + callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed); + } + void RemoveHold() override { MaybeFinish(); } + + private: + friend class ClientCallbackReaderWriterFactory; + + ClientCallbackReaderWriterImpl( + grpc::internal::Call call, ::grpc_impl::ClientContext* context, + experimental::ClientBidiReactor* reactor) + : context_(context), + call_(call), + reactor_(reactor), + start_corked_(context_->initial_metadata_corked_) { + this->BindReactor(reactor); + } + + ::grpc_impl::ClientContext* const context_; + grpc::internal::Call call_; + experimental::ClientBidiReactor* const reactor_; + + grpc::internal::CallOpSet + start_ops_; + grpc::internal::CallbackWithSuccessTag start_tag_; + bool start_corked_; + + grpc::internal::CallOpSet finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::Status finish_status_; + + grpc::internal::CallOpSet + write_ops_; + grpc::internal::CallbackWithSuccessTag write_tag_; + bool write_ops_at_start_{false}; + + grpc::internal::CallOpSet + writes_done_ops_; + grpc::internal::CallbackWithSuccessTag writes_done_tag_; + bool writes_done_ops_at_start_{false}; + + grpc::internal::CallOpSet> + read_ops_; + grpc::internal::CallbackWithSuccessTag read_tag_; + bool read_ops_at_start_{false}; + + // Minimum of 2 callbacks to pre-register for start and finish + std::atomic callbacks_outstanding_{2}; + bool started_{false}; +}; + +template +class ClientCallbackReaderWriterFactory { + public: + static void Create( + ::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + experimental::ClientBidiReactor* reactor) { + grpc::internal::Call call = + channel->CreateCall(method, context, channel->CallbackCQ()); + + ::grpc::g_core_codegen_interface->grpc_call_ref(call.call()); + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackReaderWriterImpl))) + ClientCallbackReaderWriterImpl(call, context, + reactor); + } +}; + +template +class ClientCallbackReaderImpl + : public experimental::ClientCallbackReader { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackReaderImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void MaybeFinish() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + ::grpc::Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackReaderImpl(); + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + void StartCall() override { + // This call initiates two batches, plus any backlog, each with a callback + // 1. Send initial metadata (unless corked) + recv initial metadata + // 2. Any backlog + // 3. Recv trailing metadata, on_completion callback + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + // Also set up the read tag so it doesn't have to be set up each time + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeFinish(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + if (read_ops_at_start_) { + call_.PerformOps(&read_ops_); + } + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void Read(Response* msg) override { + read_ops_.RecvMessage(msg); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (started_) { + call_.PerformOps(&read_ops_); + } else { + read_ops_at_start_ = true; + } + } + + void AddHold(int holds) override { + callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed); + } + void RemoveHold() override { MaybeFinish(); } + + private: + friend class ClientCallbackReaderFactory; + + template + ClientCallbackReaderImpl(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, + Request* request, + experimental::ClientReadReactor* reactor) + : context_(context), call_(call), reactor_(reactor) { + this->BindReactor(reactor); + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(start_ops_.SendMessagePtr(request).ok()); + start_ops_.ClientSendClose(); + } + + ::grpc_impl::ClientContext* const context_; + grpc::internal::Call call_; + experimental::ClientReadReactor* const reactor_; + + grpc::internal::CallOpSet + start_ops_; + grpc::internal::CallbackWithSuccessTag start_tag_; + + grpc::internal::CallOpSet finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::Status finish_status_; + + grpc::internal::CallOpSet> + read_ops_; + grpc::internal::CallbackWithSuccessTag read_tag_; + bool read_ops_at_start_{false}; + + // Minimum of 2 callbacks to pre-register for start and finish + std::atomic callbacks_outstanding_{2}; + bool started_{false}; +}; + +template +class ClientCallbackReaderFactory { + public: + template + static void Create(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + const Request* request, + experimental::ClientReadReactor* reactor) { + grpc::internal::Call call = + channel->CreateCall(method, context, channel->CallbackCQ()); + + ::grpc::g_core_codegen_interface->grpc_call_ref(call.call()); + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackReaderImpl))) + ClientCallbackReaderImpl(call, context, request, reactor); + } +}; + +template +class ClientCallbackWriterImpl + : public experimental::ClientCallbackWriter { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackWriterImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void MaybeFinish() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + ::grpc::Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackWriterImpl(); + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + void StartCall() override { + // This call initiates two batches, plus any backlog, each with a callback + // 1. Send initial metadata (unless corked) + recv initial metadata + // 2. Any backlog + // 3. Recv trailing metadata, on_completion callback + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + if (!start_corked_) { + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + } + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + // Also set up the read and write tags so that they don't have to be set up + // each time + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeFinish(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + + if (write_ops_at_start_) { + call_.PerformOps(&write_ops_); + } + + if (writes_done_ops_at_start_) { + call_.PerformOps(&writes_done_ops_); + } + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void Write(const Request* msg, ::grpc::WriteOptions options) override { + if (start_corked_) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(msg, options).ok()); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (started_) { + call_.PerformOps(&write_ops_); + } else { + write_ops_at_start_ = true; + } + } + void WritesDone() override { + if (start_corked_) { + writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + writes_done_ops_.ClientSendClose(); + writes_done_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWritesDoneDone(ok); + MaybeFinish(); + }, + &writes_done_ops_); + writes_done_ops_.set_core_cq_tag(&writes_done_tag_); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (started_) { + call_.PerformOps(&writes_done_ops_); + } else { + writes_done_ops_at_start_ = true; + } + } + + void AddHold(int holds) override { + callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed); + } + void RemoveHold() override { MaybeFinish(); } + + private: + friend class ClientCallbackWriterFactory; + + template + ClientCallbackWriterImpl(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, + Response* response, + experimental::ClientWriteReactor* reactor) + : context_(context), + call_(call), + reactor_(reactor), + start_corked_(context_->initial_metadata_corked_) { + this->BindReactor(reactor); + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + } + + ::grpc_impl::ClientContext* const context_; + grpc::internal::Call call_; + experimental::ClientWriteReactor* const reactor_; + + grpc::internal::CallOpSet + start_ops_; + grpc::internal::CallbackWithSuccessTag start_tag_; + bool start_corked_; + + grpc::internal::CallOpSet + finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::Status finish_status_; + + grpc::internal::CallOpSet + write_ops_; + grpc::internal::CallbackWithSuccessTag write_tag_; + bool write_ops_at_start_{false}; + + grpc::internal::CallOpSet + writes_done_ops_; + grpc::internal::CallbackWithSuccessTag writes_done_tag_; + bool writes_done_ops_at_start_{false}; + + // Minimum of 2 callbacks to pre-register for start and finish + std::atomic callbacks_outstanding_{2}; + bool started_{false}; +}; + +template +class ClientCallbackWriterFactory { + public: + template + static void Create(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, Response* response, + experimental::ClientWriteReactor* reactor) { + grpc::internal::Call call = + channel->CreateCall(method, context, channel->CallbackCQ()); + + ::grpc::g_core_codegen_interface->grpc_call_ref(call.call()); + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackWriterImpl))) + ClientCallbackWriterImpl(call, context, response, reactor); + } +}; + +class ClientCallbackUnaryImpl final : public experimental::ClientCallbackUnary { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackUnaryImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall() override { + // This call initiates two batches, each with a callback + // 1. Send initial metadata + write + writes done + recv initial metadata + // 2. Read message, recv trailing metadata + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void MaybeFinish() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + ::grpc::Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackUnaryImpl(); + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + private: + friend class ClientCallbackUnaryFactory; + + template + ClientCallbackUnaryImpl(::grpc::internal::Call call, + ::grpc_impl::ClientContext* context, Request* request, + Response* response, + experimental::ClientUnaryReactor* reactor) + : context_(context), call_(call), reactor_(reactor) { + this->BindReactor(reactor); + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(start_ops_.SendMessagePtr(request).ok()); + start_ops_.ClientSendClose(); + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + } + + ::grpc_impl::ClientContext* const context_; + grpc::internal::Call call_; + experimental::ClientUnaryReactor* const reactor_; + + grpc::internal::CallOpSet + start_ops_; + grpc::internal::CallbackWithSuccessTag start_tag_; + + grpc::internal::CallOpSet + finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + ::grpc::Status finish_status_; + + // This call will have 2 callbacks: start and finish + std::atomic callbacks_outstanding_{2}; + bool started_{false}; +}; + +class ClientCallbackUnaryFactory { + public: + template + static void Create(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + const Request* request, Response* response, + experimental::ClientUnaryReactor* reactor) { + grpc::internal::Call call = + channel->CreateCall(method, context, channel->CallbackCQ()); + + ::grpc::g_core_codegen_interface->grpc_call_ref(call.call()); + + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackUnaryImpl))) + ClientCallbackUnaryImpl(call, context, request, response, reactor); + } +}; + +} // namespace internal +} // namespace grpc_impl +#endif // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_IMPL_H diff --git a/include/grpcpp/impl/codegen/client_context_impl.h b/include/grpcpp/impl/codegen/client_context_impl.h index 8491215cf0e..638ea641bed 100644 --- a/include/grpcpp/impl/codegen/client_context_impl.h +++ b/include/grpcpp/impl/codegen/client_context_impl.h @@ -63,10 +63,19 @@ class ChannelInterface; namespace internal { class RpcMethod; -class CallOpClientRecvStatus; -class CallOpRecvInitialMetadata; template class BlockingUnaryCallImpl; +class CallOpClientRecvStatus; +class CallOpRecvInitialMetadata; +} // namespace internal + +namespace testing { +class InteropClientContextInspector; +} // namespace testing +} // namespace grpc +namespace grpc_impl { + +namespace internal { template class CallbackUnaryCallImpl; template @@ -78,6 +87,10 @@ class ClientCallbackWriterImpl; class ClientCallbackUnaryImpl; } // namespace internal +class CallCredentials; +class Channel; +class CompletionQueue; +class ServerContext; template class ClientReader; template @@ -93,17 +106,6 @@ class ClientAsyncReaderWriter; template class ClientAsyncResponseReader; -namespace testing { -class InteropClientContextInspector; -} // namespace testing -} // namespace grpc -namespace grpc_impl { - -class CallCredentials; -class Channel; -class CompletionQueue; -class ServerContext; - /// Options for \a ClientContext::FromServerContext specifying which traits from /// the \a ServerContext to propagate (copy) from it into a new \a /// ClientContext. @@ -398,30 +400,30 @@ class ClientContext { friend class ::grpc::internal::CallOpRecvInitialMetadata; friend class ::grpc_impl::Channel; template - friend class ::grpc::ClientReader; + friend class ::grpc_impl::ClientReader; template - friend class ::grpc::ClientWriter; + friend class ::grpc_impl::ClientWriter; template - friend class ::grpc::ClientReaderWriter; + friend class ::grpc_impl::ClientReaderWriter; template - friend class ::grpc::ClientAsyncReader; + friend class ::grpc_impl::ClientAsyncReader; template - friend class ::grpc::ClientAsyncWriter; + friend class ::grpc_impl::ClientAsyncWriter; template - friend class ::grpc::ClientAsyncReaderWriter; + friend class ::grpc_impl::ClientAsyncReaderWriter; template - friend class ::grpc::ClientAsyncResponseReader; + friend class ::grpc_impl::ClientAsyncResponseReader; template friend class ::grpc::internal::BlockingUnaryCallImpl; template - friend class ::grpc::internal::CallbackUnaryCallImpl; + friend class ::grpc_impl::internal::CallbackUnaryCallImpl; template - friend class ::grpc::internal::ClientCallbackReaderWriterImpl; + friend class ::grpc_impl::internal::ClientCallbackReaderWriterImpl; template - friend class ::grpc::internal::ClientCallbackReaderImpl; + friend class ::grpc_impl::internal::ClientCallbackReaderImpl; template - friend class ::grpc::internal::ClientCallbackWriterImpl; - friend class ::grpc::internal::ClientCallbackUnaryImpl; + friend class ::grpc_impl::internal::ClientCallbackWriterImpl; + friend class ::grpc_impl::internal::ClientCallbackUnaryImpl; // Used by friend class CallOpClientRecvStatus void set_debug_error_string(const grpc::string& debug_error_string) { diff --git a/include/grpcpp/impl/codegen/client_unary_call.h b/include/grpcpp/impl/codegen/client_unary_call.h index 599dd1ecac9..7f80e571c07 100644 --- a/include/grpcpp/impl/codegen/client_unary_call.h +++ b/include/grpcpp/impl/codegen/client_unary_call.h @@ -49,10 +49,10 @@ class BlockingUnaryCallImpl { BlockingUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method, grpc_impl::ClientContext* context, const InputMessage& request, OutputMessage* result) { - CompletionQueue cq(grpc_completion_queue_attributes{ + ::grpc_impl::CompletionQueue cq(grpc_completion_queue_attributes{ GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, nullptr}); // Pluckable completion queue - Call call(channel->CreateCall(method, context, &cq)); + ::grpc::internal::Call call(channel->CreateCall(method, context, &cq)); CallOpSet, CallOpClientSendClose, CallOpClientRecvStatus> diff --git a/include/grpcpp/impl/codegen/completion_queue_impl.h b/include/grpcpp/impl/codegen/completion_queue_impl.h index 0a6b0b0977b..7716a57e84a 100644 --- a/include/grpcpp/impl/codegen/completion_queue_impl.h +++ b/include/grpcpp/impl/codegen/completion_queue_impl.h @@ -47,9 +47,6 @@ class Channel; class Server; class ServerBuilder; class ServerContext; -} // namespace grpc_impl -namespace grpc { - template class ClientReader; template @@ -64,6 +61,8 @@ namespace internal { template class ServerReaderWriterBody; } // namespace internal +} // namespace grpc_impl +namespace grpc { class ChannelInterface; class ServerInterface; @@ -255,17 +254,17 @@ class CompletionQueue : private ::grpc::GrpcLibraryCodegen { // Friend synchronous wrappers so that they can access Pluck(), which is // a semi-private API geared towards the synchronous implementation. template - friend class ::grpc::ClientReader; + friend class ::grpc_impl::ClientReader; template - friend class ::grpc::ClientWriter; + friend class ::grpc_impl::ClientWriter; template - friend class ::grpc::ClientReaderWriter; + friend class ::grpc_impl::ClientReaderWriter; template - friend class ::grpc::ServerReader; + friend class ::grpc_impl::ServerReader; template - friend class ::grpc::ServerWriter; + friend class ::grpc_impl::ServerWriter; template - friend class ::grpc::internal::ServerReaderWriterBody; + friend class ::grpc_impl::internal::ServerReaderWriterBody; template friend class ::grpc::internal::RpcMethodHandler; template diff --git a/include/grpcpp/impl/codegen/server_callback.h b/include/grpcpp/impl/codegen/server_callback.h index 36ae19c5bec..cb771a662b9 100644 --- a/include/grpcpp/impl/codegen/server_callback.h +++ b/include/grpcpp/impl/codegen/server_callback.h @@ -19,1141 +19,26 @@ #ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H #define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include namespace grpc { - -// Declare base class of all reactors as internal -namespace internal { - -// Forward declarations -template -class CallbackClientStreamingHandler; -template -class CallbackServerStreamingHandler; -template -class CallbackBidiHandler; - -class ServerReactor { - public: - virtual ~ServerReactor() = default; - virtual void OnDone() = 0; - virtual void OnCancel() = 0; - - private: - friend class ::grpc_impl::ServerContext; - template - friend class CallbackClientStreamingHandler; - template - friend class CallbackServerStreamingHandler; - template - friend class CallbackBidiHandler; - - // The ServerReactor is responsible for tracking when it is safe to call - // OnCancel. This function should not be called until after OnStarted is done - // and the RPC has completed with a cancellation. This is tracked by counting - // how many of these conditions have been met and calling OnCancel when none - // remain unmet. - - void MaybeCallOnCancel() { - if (GPR_UNLIKELY(on_cancel_conditions_remaining_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - OnCancel(); - } - } - - std::atomic on_cancel_conditions_remaining_{2}; -}; - -template -class DefaultMessageHolder - : public experimental::MessageHolder { - public: - DefaultMessageHolder() { - this->set_request(&request_obj_); - this->set_response(&response_obj_); - } - void Release() override { - // the object is allocated in the call arena. - this->~DefaultMessageHolder(); - } - - private: - Request request_obj_; - Response response_obj_; -}; - -} // namespace internal - namespace experimental { - -// Forward declarations -template -class ServerReadReactor; -template -class ServerWriteReactor; -template -class ServerBidiReactor; - -// For unary RPCs, the exposed controller class is only an interface -// and the actual implementation is an internal class. -class ServerCallbackRpcController { - public: - virtual ~ServerCallbackRpcController() = default; - - // The method handler must call this function when it is done so that - // the library knows to free its resources - virtual void Finish(Status s) = 0; - - // Allow the method handler to push out the initial metadata before - // the response and status are ready - virtual void SendInitialMetadata(std::function) = 0; - - /// SetCancelCallback passes in a callback to be called when the RPC is - /// canceled for whatever reason (streaming calls have OnCancel instead). This - /// is an advanced and uncommon use with several important restrictions. This - /// function may not be called more than once on the same RPC. - /// - /// If code calls SetCancelCallback on an RPC, it must also call - /// ClearCancelCallback before calling Finish on the RPC controller. That - /// method makes sure that no cancellation callback is executed for this RPC - /// beyond the point of its return. ClearCancelCallback may be called even if - /// SetCancelCallback was not called for this RPC, and it may be called - /// multiple times. It _must_ be called if SetCancelCallback was called for - /// this RPC. - /// - /// The callback should generally be lightweight and nonblocking and primarily - /// concerned with clearing application state related to the RPC or causing - /// operations (such as cancellations) to happen on dependent RPCs. - /// - /// If the RPC is already canceled at the time that SetCancelCallback is - /// called, the callback is invoked immediately. - /// - /// The cancellation callback may be executed concurrently with the method - /// handler that invokes it but will certainly not issue or execute after the - /// return of ClearCancelCallback. If ClearCancelCallback is invoked while the - /// callback is already executing, the callback will complete its execution - /// before ClearCancelCallback takes effect. - /// - /// To preserve the orderings described above, the callback may be called - /// under a lock that is also used for ClearCancelCallback and - /// ServerContext::IsCancelled, so the callback CANNOT call either of those - /// operations on this RPC or any other function that causes those operations - /// to be called before the callback completes. - virtual void SetCancelCallback(std::function callback) = 0; - virtual void ClearCancelCallback() = 0; - - // NOTE: This is an API for advanced users who need custom allocators. - // Get and maybe mutate the allocator state associated with the current RPC. - virtual RpcAllocatorState* GetRpcAllocatorState() = 0; -}; - -// NOTE: The actual streaming object classes are provided -// as API only to support mocking. There are no implementations of -// these class interfaces in the API. -template -class ServerCallbackReader { - public: - virtual ~ServerCallbackReader() {} - virtual void Finish(Status s) = 0; - virtual void SendInitialMetadata() = 0; - virtual void Read(Request* msg) = 0; - - protected: - template - void BindReactor(ServerReadReactor* reactor) { - reactor->InternalBindReader(this); - } -}; - -template -class ServerCallbackWriter { - public: - virtual ~ServerCallbackWriter() {} - - virtual void Finish(Status s) = 0; - virtual void SendInitialMetadata() = 0; - virtual void Write(const Response* msg, WriteOptions options) = 0; - virtual void WriteAndFinish(const Response* msg, WriteOptions options, - Status s) { - // Default implementation that can/should be overridden - Write(msg, std::move(options)); - Finish(std::move(s)); - } - - protected: - template - void BindReactor(ServerWriteReactor* reactor) { - reactor->InternalBindWriter(this); - } -}; - -template -class ServerCallbackReaderWriter { - public: - virtual ~ServerCallbackReaderWriter() {} - - virtual void Finish(Status s) = 0; - virtual void SendInitialMetadata() = 0; - virtual void Read(Request* msg) = 0; - virtual void Write(const Response* msg, WriteOptions options) = 0; - virtual void WriteAndFinish(const Response* msg, WriteOptions options, - Status s) { - // Default implementation that can/should be overridden - Write(msg, std::move(options)); - Finish(std::move(s)); - } - - protected: - void BindReactor(ServerBidiReactor* reactor) { - reactor->InternalBindStream(this); - } -}; - -// The following classes are the reactor interfaces that are to be implemented -// by the user, returned as the result of the method handler for a callback -// method, and activated by the call to OnStarted. The library guarantees that -// OnStarted will be called for any reactor that has been created using a -// method handler registered on a service. No operation initiation method may be -// called until after the call to OnStarted. -// Note that none of the classes are pure; all reactions have a default empty -// reaction so that the user class only needs to override those classes that it -// cares about. - -/// \a ServerBidiReactor is the interface for a bidirectional streaming RPC. template -class ServerBidiReactor : public internal::ServerReactor { - public: - ~ServerBidiReactor() = default; - - /// Do NOT call any operation initiation method (names that start with Start) - /// until after the library has called OnStarted on this object. - - /// Send any initial metadata stored in the RPC context. If not invoked, - /// any initial metadata will be passed along with the first Write or the - /// Finish (if there are no writes). - void StartSendInitialMetadata() { stream_->SendInitialMetadata(); } - - /// Initiate a read operation. - /// - /// \param[out] req Where to eventually store the read message. Valid when - /// the library calls OnReadDone - void StartRead(Request* req) { stream_->Read(req); } - - /// Initiate a write operation. - /// - /// \param[in] resp The message to be written. The library takes temporary - /// ownership until OnWriteDone, at which point the - /// application regains ownership of resp. - void StartWrite(const Response* resp) { StartWrite(resp, WriteOptions()); } - - /// Initiate a write operation with specified options. - /// - /// \param[in] resp The message to be written. The library takes temporary - /// ownership until OnWriteDone, at which point the - /// application regains ownership of resp. - /// \param[in] options The WriteOptions to use for writing this message - void StartWrite(const Response* resp, WriteOptions options) { - stream_->Write(resp, std::move(options)); - } - - /// Initiate a write operation with specified options and final RPC Status, - /// which also causes any trailing metadata for this RPC to be sent out. - /// StartWriteAndFinish is like merging StartWriteLast and Finish into a - /// single step. A key difference, though, is that this operation doesn't have - /// an OnWriteDone reaction - it is considered complete only when OnDone is - /// available. An RPC can either have StartWriteAndFinish or Finish, but not - /// both. - /// - /// \param[in] resp The message to be written. The library takes temporary - /// ownership until Onone, at which point the application - /// regains ownership of resp. - /// \param[in] options The WriteOptions to use for writing this message - /// \param[in] s The status outcome of this RPC - void StartWriteAndFinish(const Response* resp, WriteOptions options, - Status s) { - stream_->WriteAndFinish(resp, std::move(options), std::move(s)); - } - - /// Inform system of a planned write operation with specified options, but - /// allow the library to schedule the actual write coalesced with the writing - /// of trailing metadata (which takes place on a Finish call). - /// - /// \param[in] resp The message to be written. The library takes temporary - /// ownership until OnWriteDone, at which point the - /// application regains ownership of resp. - /// \param[in] options The WriteOptions to use for writing this message - void StartWriteLast(const Response* resp, WriteOptions options) { - StartWrite(resp, std::move(options.set_last_message())); - } - - /// Indicate that the stream is to be finished and the trailing metadata and - /// RPC status are to be sent. Every RPC MUST be finished using either Finish - /// or StartWriteAndFinish (but not both), even if the RPC is already - /// cancelled. - /// - /// \param[in] s The status outcome of this RPC - void Finish(Status s) { stream_->Finish(std::move(s)); } - - /// Notify the application that a streaming RPC has started and that it is now - /// ok to call any operation initiation method. An RPC is considered started - /// after the server has received all initial metadata from the client, which - /// is a result of the client calling StartCall(). - /// - /// \param[in] context The context object now associated with this RPC - virtual void OnStarted(::grpc_impl::ServerContext* context) {} - - /// Notifies the application that an explicit StartSendInitialMetadata - /// operation completed. Not used when the sending of initial metadata - /// piggybacks onto the first write. - /// - /// \param[in] ok Was it successful? If false, no further write-side operation - /// will succeed. - virtual void OnSendInitialMetadataDone(bool ok) {} - - /// Notifies the application that a StartRead operation completed. - /// - /// \param[in] ok Was it successful? If false, no further read-side operation - /// will succeed. - virtual void OnReadDone(bool ok) {} - - /// Notifies the application that a StartWrite (or StartWriteLast) operation - /// completed. - /// - /// \param[in] ok Was it successful? If false, no further write-side operation - /// will succeed. - virtual void OnWriteDone(bool ok) {} - - /// Notifies the application that all operations associated with this RPC - /// have completed. This is an override (from the internal base class) but not - /// final, so derived classes should override it if they want to take action. - void OnDone() override {} - - /// Notifies the application that this RPC has been cancelled. This is an - /// override (from the internal base class) but not final, so derived classes - /// should override it if they want to take action. - void OnCancel() override {} - - private: - friend class ServerCallbackReaderWriter; - // May be overridden by internal implementation details. This is not a public - // customization point. - virtual void InternalBindStream( - ServerCallbackReaderWriter* stream) { - stream_ = stream; - } +using ServerReadReactor = + ::grpc_impl::experimental::ServerReadReactor; - ServerCallbackReaderWriter* stream_; -}; - -/// \a ServerReadReactor is the interface for a client-streaming RPC. template -class ServerReadReactor : public internal::ServerReactor { - public: - ~ServerReadReactor() = default; - - /// The following operation initiations are exactly like ServerBidiReactor. - void StartSendInitialMetadata() { reader_->SendInitialMetadata(); } - void StartRead(Request* req) { reader_->Read(req); } - void Finish(Status s) { reader_->Finish(std::move(s)); } - - /// Similar to ServerBidiReactor::OnStarted, except that this also provides - /// the response object that the stream fills in before calling Finish. - /// (It must be filled in if status is OK, but it may be filled in otherwise.) - /// - /// \param[in] context The context object now associated with this RPC - /// \param[in] resp The response object to be used by this RPC - virtual void OnStarted(::grpc_impl::ServerContext* context, Response* resp) {} - - /// The following notifications are exactly like ServerBidiReactor. - virtual void OnSendInitialMetadataDone(bool ok) {} - virtual void OnReadDone(bool ok) {} - void OnDone() override {} - void OnCancel() override {} - - private: - friend class ServerCallbackReader; - // May be overridden by internal implementation details. This is not a public - // customization point. - virtual void InternalBindReader(ServerCallbackReader* reader) { - reader_ = reader; - } +using ServerWriteReactor = + ::grpc_impl::experimental::ServerWriteReactor; - ServerCallbackReader* reader_; -}; - -/// \a ServerWriteReactor is the interface for a server-streaming RPC. template -class ServerWriteReactor : public internal::ServerReactor { - public: - ~ServerWriteReactor() = default; - - /// The following operation initiations are exactly like ServerBidiReactor. - void StartSendInitialMetadata() { writer_->SendInitialMetadata(); } - void StartWrite(const Response* resp) { StartWrite(resp, WriteOptions()); } - void StartWrite(const Response* resp, WriteOptions options) { - writer_->Write(resp, std::move(options)); - } - void StartWriteAndFinish(const Response* resp, WriteOptions options, - Status s) { - writer_->WriteAndFinish(resp, std::move(options), std::move(s)); - } - void StartWriteLast(const Response* resp, WriteOptions options) { - StartWrite(resp, std::move(options.set_last_message())); - } - void Finish(Status s) { writer_->Finish(std::move(s)); } - - /// Similar to ServerBidiReactor::OnStarted, except that this also provides - /// the request object sent by the client. - /// - /// \param[in] context The context object now associated with this RPC - /// \param[in] req The request object sent by the client - virtual void OnStarted(::grpc_impl::ServerContext* context, - const Request* req) {} - - /// The following notifications are exactly like ServerBidiReactor. - virtual void OnSendInitialMetadataDone(bool ok) {} - virtual void OnWriteDone(bool ok) {} - void OnDone() override {} - void OnCancel() override {} +using ServerBidiReactor = + ::grpc_impl::experimental::ServerBidiReactor; - private: - friend class ServerCallbackWriter; - // May be overridden by internal implementation details. This is not a public - // customization point. - virtual void InternalBindWriter(ServerCallbackWriter* writer) { - writer_ = writer; - } - - ServerCallbackWriter* writer_; -}; +typedef ::grpc_impl::experimental::ServerCallbackRpcController + ServerCallbackRpcController; } // namespace experimental - -namespace internal { - -template -class UnimplementedReadReactor - : public experimental::ServerReadReactor { - public: - void OnDone() override { delete this; } - void OnStarted(::grpc_impl::ServerContext*, Response*) override { - this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); - } -}; - -template -class UnimplementedWriteReactor - : public experimental::ServerWriteReactor { - public: - void OnDone() override { delete this; } - void OnStarted(::grpc_impl::ServerContext*, const Request*) override { - this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); - } -}; - -template -class UnimplementedBidiReactor - : public experimental::ServerBidiReactor { - public: - void OnDone() override { delete this; } - void OnStarted(::grpc_impl::ServerContext*) override { - this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); - } -}; - -template -class CallbackUnaryHandler : public MethodHandler { - public: - CallbackUnaryHandler( - std::function - func) - : func_(func) {} - - void SetMessageAllocator( - experimental::MessageAllocator* allocator) { - allocator_ = allocator; - } - - void RunHandler(const HandlerParameter& param) final { - // Arena allocate a controller structure (that includes request/response) - g_core_codegen_interface->grpc_call_ref(param.call->call()); - auto* allocator_state = - static_cast*>( - param.internal_data); - auto* controller = new (g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackRpcControllerImpl))) - ServerCallbackRpcControllerImpl(param.server_context, param.call, - allocator_state, - std::move(param.call_requester)); - Status status = param.status; - if (status.ok()) { - // Call the actual function handler and expect the user to call finish - CatchingCallback(func_, param.server_context, controller->request(), - controller->response(), controller); - } else { - // if deserialization failed, we need to fail the call - controller->Finish(status); - } - } - - void* Deserialize(grpc_call* call, grpc_byte_buffer* req, Status* status, - void** handler_data) final { - ByteBuffer buf; - buf.set_buffer(req); - RequestType* request = nullptr; - experimental::MessageHolder* allocator_state = - nullptr; - if (allocator_ != nullptr) { - allocator_state = allocator_->AllocateMessages(); - } else { - allocator_state = new (g_core_codegen_interface->grpc_call_arena_alloc( - call, sizeof(DefaultMessageHolder))) - DefaultMessageHolder(); - } - *handler_data = allocator_state; - request = allocator_state->request(); - *status = SerializationTraits::Deserialize(&buf, request); - buf.Release(); - if (status->ok()) { - return request; - } - // Clean up on deserialization failure. - allocator_state->Release(); - return nullptr; - } - - private: - std::function - func_; - experimental::MessageAllocator* allocator_ = - nullptr; - - // The implementation class of ServerCallbackRpcController is a private member - // of CallbackUnaryHandler since it is never exposed anywhere, and this allows - // it to take advantage of CallbackUnaryHandler's friendships. - class ServerCallbackRpcControllerImpl - : public experimental::ServerCallbackRpcController { - public: - void Finish(Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // The response is dropped if the status is not OK. - if (s.ok()) { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, - finish_ops_.SendMessagePtr(response())); - } else { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - } - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata(std::function f) override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - // TODO(vjpai): Consider taking f as a move-capture if we adopt C++14 - // and if performance of this operation matters - meta_tag_.Set(call_.call(), - [this, f](bool ok) { - f(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - // Neither SetCancelCallback nor ClearCancelCallback should affect the - // callbacks_outstanding_ count since they are paired and both must precede - // the invocation of Finish (if they are used at all) - void SetCancelCallback(std::function callback) override { - ctx_->SetCancelCallback(std::move(callback)); - } - - void ClearCancelCallback() override { ctx_->ClearCancelCallback(); } - - experimental::RpcAllocatorState* GetRpcAllocatorState() override { - return allocator_state_; - } - - private: - friend class CallbackUnaryHandler; - - ServerCallbackRpcControllerImpl( - ::grpc_impl::ServerContext* ctx, Call* call, - experimental::MessageHolder* allocator_state, - std::function call_requester) - : ctx_(ctx), - call_(*call), - allocator_state_(allocator_state), - call_requester_(std::move(call_requester)) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr); - } - - const RequestType* request() { return allocator_state_->request(); } - ResponseType* response() { return allocator_state_->response(); } - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - allocator_state_->Release(); - this->~ServerCallbackRpcControllerImpl(); // explicitly call destructor - g_core_codegen_interface->grpc_call_unref(call); - call_requester(); - } - } - - CallOpSet meta_ops_; - CallbackWithSuccessTag meta_tag_; - CallOpSet - finish_ops_; - CallbackWithSuccessTag finish_tag_; - - ::grpc_impl::ServerContext* ctx_; - Call call_; - experimental::MessageHolder* const - allocator_state_; - std::function call_requester_; - std::atomic callbacks_outstanding_{ - 2}; // reserve for Finish and CompletionOp - }; -}; - -template -class CallbackClientStreamingHandler : public MethodHandler { - public: - CallbackClientStreamingHandler( - std::function< - experimental::ServerReadReactor*()> - func) - : func_(std::move(func)) {} - void RunHandler(const HandlerParameter& param) final { - // Arena allocate a reader structure (that includes response) - g_core_codegen_interface->grpc_call_ref(param.call->call()); - - experimental::ServerReadReactor* reactor = - param.status.ok() - ? CatchingReactorCreator< - experimental::ServerReadReactor>( - func_) - : nullptr; - - if (reactor == nullptr) { - // if deserialization or reactor creator failed, we need to fail the call - reactor = new UnimplementedReadReactor; - } - - auto* reader = new (g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackReaderImpl))) - ServerCallbackReaderImpl(param.server_context, param.call, - std::move(param.call_requester), reactor); - - reader->BindReactor(reactor); - reactor->OnStarted(param.server_context, reader->response()); - // The earliest that OnCancel can be called is after OnStarted is done. - reactor->MaybeCallOnCancel(); - reader->MaybeDone(); - } - - private: - std::function*()> - func_; - - class ServerCallbackReaderImpl - : public experimental::ServerCallbackReader { - public: - void Finish(Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // The response is dropped if the status is not OK. - if (s.ok()) { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, - finish_ops_.SendMessagePtr(&resp_)); - } else { - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - } - finish_ops_.set_core_cq_tag(&finish_tag_); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - meta_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnSendInitialMetadataDone(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - void Read(RequestType* req) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - read_ops_.RecvMessage(req); - call_.PerformOps(&read_ops_); - } - - private: - friend class CallbackClientStreamingHandler; - - ServerCallbackReaderImpl( - ::grpc_impl::ServerContext* ctx, Call* call, - std::function call_requester, - experimental::ServerReadReactor* reactor) - : ctx_(ctx), - call_(*call), - call_requester_(std::move(call_requester)), - reactor_(reactor) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); - read_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadDone(ok); - MaybeDone(); - }, - &read_ops_); - read_ops_.set_core_cq_tag(&read_tag_); - } - - ~ServerCallbackReaderImpl() {} - - ResponseType* response() { return &resp_; } - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - reactor_->OnDone(); - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - this->~ServerCallbackReaderImpl(); // explicitly call destructor - g_core_codegen_interface->grpc_call_unref(call); - call_requester(); - } - } - - CallOpSet meta_ops_; - CallbackWithSuccessTag meta_tag_; - CallOpSet - finish_ops_; - CallbackWithSuccessTag finish_tag_; - CallOpSet> read_ops_; - CallbackWithSuccessTag read_tag_; - - ::grpc_impl::ServerContext* ctx_; - Call call_; - ResponseType resp_; - std::function call_requester_; - experimental::ServerReadReactor* reactor_; - std::atomic callbacks_outstanding_{ - 3}; // reserve for OnStarted, Finish, and CompletionOp - }; -}; - -template -class CallbackServerStreamingHandler : public MethodHandler { - public: - CallbackServerStreamingHandler( - std::function< - experimental::ServerWriteReactor*()> - func) - : func_(std::move(func)) {} - void RunHandler(const HandlerParameter& param) final { - // Arena allocate a writer structure - g_core_codegen_interface->grpc_call_ref(param.call->call()); - - experimental::ServerWriteReactor* reactor = - param.status.ok() - ? CatchingReactorCreator< - experimental::ServerWriteReactor>( - func_) - : nullptr; - - if (reactor == nullptr) { - // if deserialization or reactor creator failed, we need to fail the call - reactor = new UnimplementedWriteReactor; - } - - auto* writer = new (g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackWriterImpl))) - ServerCallbackWriterImpl(param.server_context, param.call, - static_cast(param.request), - std::move(param.call_requester), reactor); - writer->BindReactor(reactor); - reactor->OnStarted(param.server_context, writer->request()); - // The earliest that OnCancel can be called is after OnStarted is done. - reactor->MaybeCallOnCancel(); - writer->MaybeDone(); - } - - void* Deserialize(grpc_call* call, grpc_byte_buffer* req, Status* status, - void** handler_data) final { - ByteBuffer buf; - buf.set_buffer(req); - auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( - call, sizeof(RequestType))) RequestType(); - *status = SerializationTraits::Deserialize(&buf, request); - buf.Release(); - if (status->ok()) { - return request; - } - request->~RequestType(); - return nullptr; - } - - private: - std::function*()> - func_; - - class ServerCallbackWriterImpl - : public experimental::ServerCallbackWriter { - public: - void Finish(Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - finish_ops_.set_core_cq_tag(&finish_tag_); - - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - meta_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnSendInitialMetadataDone(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - void Write(const ResponseType* resp, WriteOptions options) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (options.is_last_message()) { - options.set_buffer_hint(); - } - if (!ctx_->sent_initial_metadata_) { - write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - write_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); - call_.PerformOps(&write_ops_); - } - - void WriteAndFinish(const ResponseType* resp, WriteOptions options, - Status s) override { - // This combines the write into the finish callback - // Don't send any message if the status is bad - if (s.ok()) { - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); - } - Finish(std::move(s)); - } - - private: - friend class CallbackServerStreamingHandler; - - ServerCallbackWriterImpl( - ::grpc_impl::ServerContext* ctx, Call* call, const RequestType* req, - std::function call_requester, - experimental::ServerWriteReactor* reactor) - : ctx_(ctx), - call_(*call), - req_(req), - call_requester_(std::move(call_requester)), - reactor_(reactor) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); - write_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWriteDone(ok); - MaybeDone(); - }, - &write_ops_); - write_ops_.set_core_cq_tag(&write_tag_); - } - ~ServerCallbackWriterImpl() { req_->~RequestType(); } - - const RequestType* request() { return req_; } - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - reactor_->OnDone(); - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - this->~ServerCallbackWriterImpl(); // explicitly call destructor - g_core_codegen_interface->grpc_call_unref(call); - call_requester(); - } - } - - CallOpSet meta_ops_; - CallbackWithSuccessTag meta_tag_; - CallOpSet - finish_ops_; - CallbackWithSuccessTag finish_tag_; - CallOpSet write_ops_; - CallbackWithSuccessTag write_tag_; - - ::grpc_impl::ServerContext* ctx_; - Call call_; - const RequestType* req_; - std::function call_requester_; - experimental::ServerWriteReactor* reactor_; - std::atomic callbacks_outstanding_{ - 3}; // reserve for OnStarted, Finish, and CompletionOp - }; -}; - -template -class CallbackBidiHandler : public MethodHandler { - public: - CallbackBidiHandler( - std::function< - experimental::ServerBidiReactor*()> - func) - : func_(std::move(func)) {} - void RunHandler(const HandlerParameter& param) final { - g_core_codegen_interface->grpc_call_ref(param.call->call()); - - experimental::ServerBidiReactor* reactor = - param.status.ok() - ? CatchingReactorCreator< - experimental::ServerBidiReactor>( - func_) - : nullptr; - - if (reactor == nullptr) { - // if deserialization or reactor creator failed, we need to fail the call - reactor = new UnimplementedBidiReactor; - } - - auto* stream = new (g_core_codegen_interface->grpc_call_arena_alloc( - param.call->call(), sizeof(ServerCallbackReaderWriterImpl))) - ServerCallbackReaderWriterImpl(param.server_context, param.call, - std::move(param.call_requester), - reactor); - - stream->BindReactor(reactor); - reactor->OnStarted(param.server_context); - // The earliest that OnCancel can be called is after OnStarted is done. - reactor->MaybeCallOnCancel(); - stream->MaybeDone(); - } - - private: - std::function*()> - func_; - - class ServerCallbackReaderWriterImpl - : public experimental::ServerCallbackReaderWriter { - public: - void Finish(Status s) override { - finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, - &finish_ops_); - finish_ops_.set_core_cq_tag(&finish_tag_); - - if (!ctx_->sent_initial_metadata_) { - finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - finish_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); - call_.PerformOps(&finish_ops_); - } - - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - meta_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnSendInitialMetadataDone(ok); - MaybeDone(); - }, - &meta_ops_); - meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - meta_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - meta_ops_.set_core_cq_tag(&meta_tag_); - call_.PerformOps(&meta_ops_); - } - - void Write(const ResponseType* resp, WriteOptions options) override { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - if (options.is_last_message()) { - options.set_buffer_hint(); - } - if (!ctx_->sent_initial_metadata_) { - write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - write_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - // TODO(vjpai): don't assert - GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); - call_.PerformOps(&write_ops_); - } - - void WriteAndFinish(const ResponseType* resp, WriteOptions options, - 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 { - callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); - read_ops_.RecvMessage(req); - call_.PerformOps(&read_ops_); - } - - private: - friend class CallbackBidiHandler; - - ServerCallbackReaderWriterImpl( - ::grpc_impl::ServerContext* ctx, Call* call, - std::function call_requester, - experimental::ServerBidiReactor* reactor) - : ctx_(ctx), - call_(*call), - call_requester_(std::move(call_requester)), - reactor_(reactor) { - ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); - write_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnWriteDone(ok); - MaybeDone(); - }, - &write_ops_); - write_ops_.set_core_cq_tag(&write_tag_); - read_tag_.Set(call_.call(), - [this](bool ok) { - reactor_->OnReadDone(ok); - MaybeDone(); - }, - &read_ops_); - read_ops_.set_core_cq_tag(&read_tag_); - } - ~ServerCallbackReaderWriterImpl() {} - - void MaybeDone() { - if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( - 1, std::memory_order_acq_rel) == 1)) { - reactor_->OnDone(); - grpc_call* call = call_.call(); - auto call_requester = std::move(call_requester_); - this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor - g_core_codegen_interface->grpc_call_unref(call); - call_requester(); - } - } - - CallOpSet meta_ops_; - CallbackWithSuccessTag meta_tag_; - CallOpSet - finish_ops_; - CallbackWithSuccessTag finish_tag_; - CallOpSet write_ops_; - CallbackWithSuccessTag write_tag_; - CallOpSet> read_ops_; - CallbackWithSuccessTag read_tag_; - - ::grpc_impl::ServerContext* ctx_; - Call call_; - std::function call_requester_; - experimental::ServerBidiReactor* reactor_; - std::atomic callbacks_outstanding_{ - 3}; // reserve for OnStarted, Finish, and CompletionOp - }; -}; - -} // namespace internal - } // namespace grpc #endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H diff --git a/include/grpcpp/impl/codegen/server_callback_impl.h b/include/grpcpp/impl/codegen/server_callback_impl.h new file mode 100644 index 00000000000..08ecdfd6497 --- /dev/null +++ b/include/grpcpp/impl/codegen/server_callback_impl.h @@ -0,0 +1,1186 @@ +/* + * + * 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_IMPL_H +#define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_IMPL_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc_impl { + +// Declare base class of all reactors as internal +namespace internal { + +// Forward declarations +template +class CallbackClientStreamingHandler; +template +class CallbackServerStreamingHandler; +template +class CallbackBidiHandler; + +class ServerReactor { + public: + virtual ~ServerReactor() = default; + virtual void OnDone() = 0; + virtual void OnCancel() = 0; + + private: + friend class ::grpc_impl::ServerContext; + template + friend class CallbackClientStreamingHandler; + template + friend class CallbackServerStreamingHandler; + template + friend class CallbackBidiHandler; + + // The ServerReactor is responsible for tracking when it is safe to call + // OnCancel. This function should not be called until after OnStarted is done + // and the RPC has completed with a cancellation. This is tracked by counting + // how many of these conditions have been met and calling OnCancel when none + // remain unmet. + + void MaybeCallOnCancel() { + if (GPR_UNLIKELY(on_cancel_conditions_remaining_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + OnCancel(); + } + } + + std::atomic on_cancel_conditions_remaining_{2}; +}; + +template +class DefaultMessageHolder + : public ::grpc::experimental::MessageHolder { + public: + DefaultMessageHolder() { + this->set_request(&request_obj_); + this->set_response(&response_obj_); + } + void Release() override { + // the object is allocated in the call arena. + this->~DefaultMessageHolder(); + } + + private: + Request request_obj_; + Response response_obj_; +}; + +} // namespace internal + +namespace experimental { + +// Forward declarations +template +class ServerReadReactor; +template +class ServerWriteReactor; +template +class ServerBidiReactor; + +// For unary RPCs, the exposed controller class is only an interface +// and the actual implementation is an internal class. +class ServerCallbackRpcController { + public: + virtual ~ServerCallbackRpcController() = default; + + // The method handler must call this function when it is done so that + // the library knows to free its resources + virtual void Finish(::grpc::Status s) = 0; + + // Allow the method handler to push out the initial metadata before + // the response and status are ready + virtual void SendInitialMetadata(std::function) = 0; + + /// SetCancelCallback passes in a callback to be called when the RPC is + /// canceled for whatever reason (streaming calls have OnCancel instead). This + /// is an advanced and uncommon use with several important restrictions. This + /// function may not be called more than once on the same RPC. + /// + /// If code calls SetCancelCallback on an RPC, it must also call + /// ClearCancelCallback before calling Finish on the RPC controller. That + /// method makes sure that no cancellation callback is executed for this RPC + /// beyond the point of its return. ClearCancelCallback may be called even if + /// SetCancelCallback was not called for this RPC, and it may be called + /// multiple times. It _must_ be called if SetCancelCallback was called for + /// this RPC. + /// + /// The callback should generally be lightweight and nonblocking and primarily + /// concerned with clearing application state related to the RPC or causing + /// operations (such as cancellations) to happen on dependent RPCs. + /// + /// If the RPC is already canceled at the time that SetCancelCallback is + /// called, the callback is invoked immediately. + /// + /// The cancellation callback may be executed concurrently with the method + /// handler that invokes it but will certainly not issue or execute after the + /// return of ClearCancelCallback. If ClearCancelCallback is invoked while the + /// callback is already executing, the callback will complete its execution + /// before ClearCancelCallback takes effect. + /// + /// To preserve the orderings described above, the callback may be called + /// under a lock that is also used for ClearCancelCallback and + /// ServerContext::IsCancelled, so the callback CANNOT call either of those + /// operations on this RPC or any other function that causes those operations + /// to be called before the callback completes. + virtual void SetCancelCallback(std::function callback) = 0; + virtual void ClearCancelCallback() = 0; + + // NOTE: This is an API for advanced users who need custom allocators. + // Get and maybe mutate the allocator state associated with the current RPC. + virtual grpc::experimental::RpcAllocatorState* GetRpcAllocatorState() = 0; +}; + +// NOTE: The actual streaming object classes are provided +// as API only to support mocking. There are no implementations of +// these class interfaces in the API. +template +class ServerCallbackReader { + public: + virtual ~ServerCallbackReader() {} + virtual void Finish(::grpc::Status s) = 0; + virtual void SendInitialMetadata() = 0; + virtual void Read(Request* msg) = 0; + + protected: + template + void BindReactor(ServerReadReactor* reactor) { + reactor->InternalBindReader(this); + } +}; + +template +class ServerCallbackWriter { + public: + virtual ~ServerCallbackWriter() {} + + virtual void Finish(::grpc::Status s) = 0; + virtual void SendInitialMetadata() = 0; + virtual void Write(const Response* msg, ::grpc::WriteOptions options) = 0; + virtual void WriteAndFinish(const Response* msg, ::grpc::WriteOptions options, + ::grpc::Status s) { + // Default implementation that can/should be overridden + Write(msg, std::move(options)); + Finish(std::move(s)); + } + + protected: + template + void BindReactor(ServerWriteReactor* reactor) { + reactor->InternalBindWriter(this); + } +}; + +template +class ServerCallbackReaderWriter { + public: + virtual ~ServerCallbackReaderWriter() {} + + virtual void Finish(::grpc::Status s) = 0; + virtual void SendInitialMetadata() = 0; + virtual void Read(Request* msg) = 0; + virtual void Write(const Response* msg, ::grpc::WriteOptions options) = 0; + virtual void WriteAndFinish(const Response* msg, ::grpc::WriteOptions options, + ::grpc::Status s) { + // Default implementation that can/should be overridden + Write(msg, std::move(options)); + Finish(std::move(s)); + } + + protected: + void BindReactor(ServerBidiReactor* reactor) { + reactor->InternalBindStream(this); + } +}; + +// The following classes are the reactor interfaces that are to be implemented +// by the user, returned as the result of the method handler for a callback +// method, and activated by the call to OnStarted. The library guarantees that +// OnStarted will be called for any reactor that has been created using a +// method handler registered on a service. No operation initiation method may be +// called until after the call to OnStarted. +// Note that none of the classes are pure; all reactions have a default empty +// reaction so that the user class only needs to override those classes that it +// cares about. + +/// \a ServerBidiReactor is the interface for a bidirectional streaming RPC. +template +class ServerBidiReactor : public internal::ServerReactor { + public: + ~ServerBidiReactor() = default; + + /// Do NOT call any operation initiation method (names that start with Start) + /// until after the library has called OnStarted on this object. + + /// Send any initial metadata stored in the RPC context. If not invoked, + /// any initial metadata will be passed along with the first Write or the + /// Finish (if there are no writes). + void StartSendInitialMetadata() { stream_->SendInitialMetadata(); } + + /// Initiate a read operation. + /// + /// \param[out] req Where to eventually store the read message. Valid when + /// the library calls OnReadDone + void StartRead(Request* req) { stream_->Read(req); } + + /// Initiate a write operation. + /// + /// \param[in] resp The message to be written. The library takes temporary + /// ownership until OnWriteDone, at which point the + /// application regains ownership of resp. + void StartWrite(const Response* resp) { + StartWrite(resp, ::grpc::WriteOptions()); + } + + /// Initiate a write operation with specified options. + /// + /// \param[in] resp The message to be written. The library takes temporary + /// ownership until OnWriteDone, at which point the + /// application regains ownership of resp. + /// \param[in] options The WriteOptions to use for writing this message + void StartWrite(const Response* resp, ::grpc::WriteOptions options) { + stream_->Write(resp, std::move(options)); + } + + /// Initiate a write operation with specified options and final RPC Status, + /// which also causes any trailing metadata for this RPC to be sent out. + /// StartWriteAndFinish is like merging StartWriteLast and Finish into a + /// single step. A key difference, though, is that this operation doesn't have + /// an OnWriteDone reaction - it is considered complete only when OnDone is + /// available. An RPC can either have StartWriteAndFinish or Finish, but not + /// both. + /// + /// \param[in] resp The message to be written. The library takes temporary + /// ownership until Onone, at which point the application + /// regains ownership of resp. + /// \param[in] options The WriteOptions to use for writing this message + /// \param[in] s The status outcome of this RPC + void StartWriteAndFinish(const Response* resp, ::grpc::WriteOptions options, + ::grpc::Status s) { + stream_->WriteAndFinish(resp, std::move(options), std::move(s)); + } + + /// Inform system of a planned write operation with specified options, but + /// allow the library to schedule the actual write coalesced with the writing + /// of trailing metadata (which takes place on a Finish call). + /// + /// \param[in] resp The message to be written. The library takes temporary + /// ownership until OnWriteDone, at which point the + /// application regains ownership of resp. + /// \param[in] options The WriteOptions to use for writing this message + void StartWriteLast(const Response* resp, ::grpc::WriteOptions options) { + StartWrite(resp, std::move(options.set_last_message())); + } + + /// Indicate that the stream is to be finished and the trailing metadata and + /// RPC status are to be sent. Every RPC MUST be finished using either Finish + /// or StartWriteAndFinish (but not both), even if the RPC is already + /// cancelled. + /// + /// \param[in] s The status outcome of this RPC + void Finish(::grpc::Status s) { stream_->Finish(std::move(s)); } + + /// Notify the application that a streaming RPC has started and that it is now + /// ok to call any operation initiation method. An RPC is considered started + /// after the server has received all initial metadata from the client, which + /// is a result of the client calling StartCall(). + /// + /// \param[in] context The context object now associated with this RPC + virtual void OnStarted(::grpc_impl::ServerContext* context) {} + + /// Notifies the application that an explicit StartSendInitialMetadata + /// operation completed. Not used when the sending of initial metadata + /// piggybacks onto the first write. + /// + /// \param[in] ok Was it successful? If false, no further write-side operation + /// will succeed. + virtual void OnSendInitialMetadataDone(bool ok) {} + + /// Notifies the application that a StartRead operation completed. + /// + /// \param[in] ok Was it successful? If false, no further read-side operation + /// will succeed. + virtual void OnReadDone(bool ok) {} + + /// Notifies the application that a StartWrite (or StartWriteLast) operation + /// completed. + /// + /// \param[in] ok Was it successful? If false, no further write-side operation + /// will succeed. + virtual void OnWriteDone(bool ok) {} + + /// Notifies the application that all operations associated with this RPC + /// have completed. This is an override (from the internal base class) but not + /// final, so derived classes should override it if they want to take action. + void OnDone() override {} + + /// Notifies the application that this RPC has been cancelled. This is an + /// override (from the internal base class) but not final, so derived classes + /// should override it if they want to take action. + void OnCancel() override {} + + private: + friend class ServerCallbackReaderWriter; + // May be overridden by internal implementation details. This is not a public + // customization point. + virtual void InternalBindStream( + ServerCallbackReaderWriter* stream) { + stream_ = stream; + } + + ServerCallbackReaderWriter* stream_; +}; + +/// \a ServerReadReactor is the interface for a client-streaming RPC. +template +class ServerReadReactor : public internal::ServerReactor { + public: + ~ServerReadReactor() = default; + + /// The following operation initiations are exactly like ServerBidiReactor. + void StartSendInitialMetadata() { reader_->SendInitialMetadata(); } + void StartRead(Request* req) { reader_->Read(req); } + void Finish(::grpc::Status s) { reader_->Finish(std::move(s)); } + + /// Similar to ServerBidiReactor::OnStarted, except that this also provides + /// the response object that the stream fills in before calling Finish. + /// (It must be filled in if status is OK, but it may be filled in otherwise.) + /// + /// \param[in] context The context object now associated with this RPC + /// \param[in] resp The response object to be used by this RPC + virtual void OnStarted(::grpc_impl::ServerContext* context, Response* resp) {} + + /// The following notifications are exactly like ServerBidiReactor. + virtual void OnSendInitialMetadataDone(bool ok) {} + virtual void OnReadDone(bool ok) {} + void OnDone() override {} + void OnCancel() override {} + + private: + friend class ServerCallbackReader; + // May be overridden by internal implementation details. This is not a public + // customization point. + virtual void InternalBindReader(ServerCallbackReader* reader) { + reader_ = reader; + } + + ServerCallbackReader* reader_; +}; + +/// \a ServerWriteReactor is the interface for a server-streaming RPC. +template +class ServerWriteReactor : public internal::ServerReactor { + public: + ~ServerWriteReactor() = default; + + /// The following operation initiations are exactly like ServerBidiReactor. + void StartSendInitialMetadata() { writer_->SendInitialMetadata(); } + void StartWrite(const Response* resp) { + StartWrite(resp, ::grpc::WriteOptions()); + } + void StartWrite(const Response* resp, ::grpc::WriteOptions options) { + writer_->Write(resp, std::move(options)); + } + void StartWriteAndFinish(const Response* resp, ::grpc::WriteOptions options, + ::grpc::Status s) { + writer_->WriteAndFinish(resp, std::move(options), std::move(s)); + } + void StartWriteLast(const Response* resp, ::grpc::WriteOptions options) { + StartWrite(resp, std::move(options.set_last_message())); + } + void Finish(::grpc::Status s) { writer_->Finish(std::move(s)); } + + /// Similar to ServerBidiReactor::OnStarted, except that this also provides + /// the request object sent by the client. + /// + /// \param[in] context The context object now associated with this RPC + /// \param[in] req The request object sent by the client + virtual void OnStarted(::grpc_impl::ServerContext* context, + const Request* req) {} + + /// The following notifications are exactly like ServerBidiReactor. + virtual void OnSendInitialMetadataDone(bool ok) {} + virtual void OnWriteDone(bool ok) {} + void OnDone() override {} + void OnCancel() override {} + + private: + friend class ServerCallbackWriter; + // May be overridden by internal implementation details. This is not a public + // customization point. + virtual void InternalBindWriter(ServerCallbackWriter* writer) { + writer_ = writer; + } + + ServerCallbackWriter* writer_; +}; + +} // namespace experimental + +namespace internal { + +template +class UnimplementedReadReactor + : public experimental::ServerReadReactor { + public: + void OnDone() override { delete this; } + void OnStarted(::grpc_impl::ServerContext*, Response*) override { + this->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } +}; + +template +class UnimplementedWriteReactor + : public experimental::ServerWriteReactor { + public: + void OnDone() override { delete this; } + void OnStarted(::grpc_impl::ServerContext*, const Request*) override { + this->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } +}; + +template +class UnimplementedBidiReactor + : public experimental::ServerBidiReactor { + public: + void OnDone() override { delete this; } + void OnStarted(::grpc_impl::ServerContext*) override { + this->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); + } +}; + +template +class CallbackUnaryHandler : public grpc::internal::MethodHandler { + public: + CallbackUnaryHandler( + std::function + func) + : func_(func) {} + + void SetMessageAllocator( + ::grpc::experimental::MessageAllocator* + allocator) { + allocator_ = allocator; + } + + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a controller structure (that includes request/response) + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + auto* allocator_state = static_cast< + grpc::experimental::MessageHolder*>( + param.internal_data); + auto* controller = + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackRpcControllerImpl))) + ServerCallbackRpcControllerImpl(param.server_context, param.call, + allocator_state, + std::move(param.call_requester)); + ::grpc::Status status = param.status; + if (status.ok()) { + // Call the actual function handler and expect the user to call finish + grpc::internal::CatchingCallback(func_, param.server_context, + controller->request(), + controller->response(), controller); + } else { + // if deserialization failed, we need to fail the call + controller->Finish(status); + } + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + ::grpc::Status* status, void** handler_data) final { + grpc::ByteBuffer buf; + buf.set_buffer(req); + RequestType* request = nullptr; + ::grpc::experimental::MessageHolder* + allocator_state = nullptr; + if (allocator_ != nullptr) { + allocator_state = allocator_->AllocateMessages(); + } else { + allocator_state = + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(DefaultMessageHolder))) + DefaultMessageHolder(); + } + *handler_data = allocator_state; + request = allocator_state->request(); + *status = + ::grpc::SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + // Clean up on deserialization failure. + allocator_state->Release(); + return nullptr; + } + + private: + std::function + func_; + grpc::experimental::MessageAllocator* allocator_ = + nullptr; + + // The implementation class of ServerCallbackRpcController is a private member + // of CallbackUnaryHandler since it is never exposed anywhere, and this allows + // it to take advantage of CallbackUnaryHandler's friendships. + class ServerCallbackRpcControllerImpl + : public experimental::ServerCallbackRpcController { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (s.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessagePtr(response())); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + } + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata(std::function f) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + // TODO(vjpai): Consider taking f as a move-capture if we adopt C++14 + // and if performance of this operation matters + meta_tag_.Set(call_.call(), + [this, f](bool ok) { + f(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + // Neither SetCancelCallback nor ClearCancelCallback should affect the + // callbacks_outstanding_ count since they are paired and both must precede + // the invocation of Finish (if they are used at all) + void SetCancelCallback(std::function callback) override { + ctx_->SetCancelCallback(std::move(callback)); + } + + void ClearCancelCallback() override { ctx_->ClearCancelCallback(); } + + grpc::experimental::RpcAllocatorState* GetRpcAllocatorState() override { + return allocator_state_; + } + + private: + friend class CallbackUnaryHandler; + + ServerCallbackRpcControllerImpl( + ServerContext* ctx, ::grpc::internal::Call* call, + ::grpc::experimental::MessageHolder* + allocator_state, + std::function call_requester) + : ctx_(ctx), + call_(*call), + allocator_state_(allocator_state), + call_requester_(std::move(call_requester)) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr); + } + + const RequestType* request() { return allocator_state_->request(); } + ResponseType* response() { return allocator_state_->response(); } + + void MaybeDone() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + allocator_state_->Release(); + this->~ServerCallbackRpcControllerImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + grpc::internal::CallOpSet + meta_ops_; + grpc::internal::CallbackWithSuccessTag meta_tag_; + grpc::internal::CallOpSet + finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + + ::grpc_impl::ServerContext* ctx_; + grpc::internal::Call call_; + grpc::experimental::MessageHolder* const + allocator_state_; + std::function call_requester_; + std::atomic callbacks_outstanding_{ + 2}; // reserve for Finish and CompletionOp + }; +}; + +template +class CallbackClientStreamingHandler : public grpc::internal::MethodHandler { + public: + CallbackClientStreamingHandler( + std::function< + experimental::ServerReadReactor*()> + func) + : func_(std::move(func)) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a reader structure (that includes response) + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + + experimental::ServerReadReactor* reactor = + param.status.ok() + ? ::grpc::internal::CatchingReactorCreator< + experimental::ServerReadReactor>( + func_) + : nullptr; + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new UnimplementedReadReactor; + } + + auto* reader = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackReaderImpl))) + ServerCallbackReaderImpl(param.server_context, param.call, + std::move(param.call_requester), reactor); + + reader->BindReactor(reactor); + reactor->OnStarted(param.server_context, reader->response()); + // The earliest that OnCancel can be called is after OnStarted is done. + reactor->MaybeCallOnCancel(); + reader->MaybeDone(); + } + + private: + std::function*()> + func_; + + class ServerCallbackReaderImpl + : public experimental::ServerCallbackReader { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (s.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessagePtr(&resp_)); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + } + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Read(RequestType* req) override { + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + read_ops_.RecvMessage(req); + call_.PerformOps(&read_ops_); + } + + private: + friend class CallbackClientStreamingHandler; + + ServerCallbackReaderImpl( + ::grpc_impl::ServerContext* ctx, grpc::internal::Call* call, + std::function call_requester, + experimental::ServerReadReactor* reactor) + : ctx_(ctx), + call_(*call), + call_requester_(std::move(call_requester)), + reactor_(reactor) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeDone(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + } + + ~ServerCallbackReaderImpl() {} + + ResponseType* response() { return &resp_; } + + void MaybeDone() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + reactor_->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackReaderImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + grpc::internal::CallOpSet + meta_ops_; + grpc::internal::CallbackWithSuccessTag meta_tag_; + grpc::internal::CallOpSet + finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + grpc::internal::CallOpSet> + read_ops_; + grpc::internal::CallbackWithSuccessTag read_tag_; + + ::grpc_impl::ServerContext* ctx_; + grpc::internal::Call call_; + ResponseType resp_; + std::function call_requester_; + experimental::ServerReadReactor* reactor_; + std::atomic callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +template +class CallbackServerStreamingHandler : public grpc::internal::MethodHandler { + public: + CallbackServerStreamingHandler( + std::function< + experimental::ServerWriteReactor*()> + func) + : func_(std::move(func)) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a writer structure + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + + experimental::ServerWriteReactor* reactor = + param.status.ok() + ? ::grpc::internal::CatchingReactorCreator< + experimental::ServerWriteReactor>( + func_) + : nullptr; + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new UnimplementedWriteReactor; + } + + auto* writer = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackWriterImpl))) + ServerCallbackWriterImpl(param.server_context, param.call, + static_cast(param.request), + std::move(param.call_requester), reactor); + writer->BindReactor(reactor); + reactor->OnStarted(param.server_context, writer->request()); + // The earliest that OnCancel can be called is after OnStarted is done. + reactor->MaybeCallOnCancel(); + writer->MaybeDone(); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + ::grpc::Status* status, void** handler_data) final { + ::grpc::ByteBuffer buf; + buf.set_buffer(req); + auto* request = + new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(RequestType))) RequestType(); + *status = + ::grpc::SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + request->~RequestType(); + return nullptr; + } + + private: + std::function*()> + func_; + + class ServerCallbackWriterImpl + : public experimental::ServerCallbackWriter { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Write(const ResponseType* resp, + ::grpc::WriteOptions options) override { + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + write_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WriteAndFinish(const ResponseType* resp, ::grpc::WriteOptions options, + ::grpc::Status s) override { + // This combines the write into the finish callback + // Don't send any message if the status is bad + if (s.ok()) { + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); + } + Finish(std::move(s)); + } + + private: + friend class CallbackServerStreamingHandler; + + ServerCallbackWriterImpl( + ::grpc_impl::ServerContext* ctx, grpc::internal::Call* call, + const RequestType* req, std::function call_requester, + experimental::ServerWriteReactor* reactor) + : ctx_(ctx), + call_(*call), + req_(req), + call_requester_(std::move(call_requester)), + reactor_(reactor) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeDone(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + } + ~ServerCallbackWriterImpl() { req_->~RequestType(); } + + const RequestType* request() { return req_; } + + void MaybeDone() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + reactor_->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackWriterImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + grpc::internal::CallOpSet + meta_ops_; + grpc::internal::CallbackWithSuccessTag meta_tag_; + grpc::internal::CallOpSet + finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + grpc::internal::CallOpSet + write_ops_; + grpc::internal::CallbackWithSuccessTag write_tag_; + + ::grpc_impl::ServerContext* ctx_; + grpc::internal::Call call_; + const RequestType* req_; + std::function call_requester_; + experimental::ServerWriteReactor* reactor_; + std::atomic callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +template +class CallbackBidiHandler : public grpc::internal::MethodHandler { + public: + CallbackBidiHandler( + std::function< + experimental::ServerBidiReactor*()> + func) + : func_(std::move(func)) {} + void RunHandler(const HandlerParameter& param) final { + ::grpc::g_core_codegen_interface->grpc_call_ref(param.call->call()); + + experimental::ServerBidiReactor* reactor = + param.status.ok() + ? ::grpc::internal::CatchingReactorCreator< + experimental::ServerBidiReactor>( + func_) + : nullptr; + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new UnimplementedBidiReactor; + } + + auto* stream = new (::grpc::g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackReaderWriterImpl))) + ServerCallbackReaderWriterImpl(param.server_context, param.call, + std::move(param.call_requester), + reactor); + + stream->BindReactor(reactor); + reactor->OnStarted(param.server_context); + // The earliest that OnCancel can be called is after OnStarted is done. + reactor->MaybeCallOnCancel(); + stream->MaybeDone(); + } + + private: + std::function*()> + func_; + + class ServerCallbackReaderWriterImpl + : public experimental::ServerCallbackReaderWriter { + public: + void Finish(::grpc::Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Write(const ResponseType* resp, + ::grpc::WriteOptions options) override { + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + write_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessagePtr(resp, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WriteAndFinish(const ResponseType* resp, ::grpc::WriteOptions options, + ::grpc::Status s) override { + // Don't send any message if the status is bad + if (s.ok()) { + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(finish_ops_.SendMessagePtr(resp, options).ok()); + } + Finish(std::move(s)); + } + + void Read(RequestType* req) override { + callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); + read_ops_.RecvMessage(req); + call_.PerformOps(&read_ops_); + } + + private: + friend class CallbackBidiHandler; + + ServerCallbackReaderWriterImpl( + ::grpc_impl::ServerContext* ctx, grpc::internal::Call* call, + std::function call_requester, + experimental::ServerBidiReactor* reactor) + : ctx_(ctx), + call_(*call), + call_requester_(std::move(call_requester)), + reactor_(reactor) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeDone(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeDone(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + } + ~ServerCallbackReaderWriterImpl() {} + + void MaybeDone() { + if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub( + 1, std::memory_order_acq_rel) == 1)) { + reactor_->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor + ::grpc::g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + grpc::internal::CallOpSet + meta_ops_; + grpc::internal::CallbackWithSuccessTag meta_tag_; + grpc::internal::CallOpSet + finish_ops_; + grpc::internal::CallbackWithSuccessTag finish_tag_; + grpc::internal::CallOpSet + write_ops_; + grpc::internal::CallbackWithSuccessTag write_tag_; + grpc::internal::CallOpSet> + read_ops_; + grpc::internal::CallbackWithSuccessTag read_tag_; + + ::grpc_impl::ServerContext* ctx_; + grpc::internal::Call call_; + std::function call_requester_; + experimental::ServerBidiReactor* reactor_; + std::atomic callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +} // namespace internal + +} // namespace grpc_impl + +#endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_IMPL_H diff --git a/include/grpcpp/impl/codegen/server_context_impl.h b/include/grpcpp/impl/codegen/server_context_impl.h index 9497735eacc..72137f153b1 100644 --- a/include/grpcpp/impl/codegen/server_context_impl.h +++ b/include/grpcpp/impl/codegen/server_context_impl.h @@ -44,10 +44,6 @@ namespace grpc_impl { class ClientContext; class CompletionQueue; class Server; -} // namespace grpc_impl -namespace grpc { -class GenericServerContext; -class ServerInterface; template class ServerAsyncReader; template @@ -62,14 +58,6 @@ template class ServerWriter; namespace internal { -template -class ServerReaderWriterBody; -template -class RpcMethodHandler; -template -class ClientStreamingHandler; -template -class ServerStreamingHandler; template class BidiStreamingHandler; template @@ -80,12 +68,28 @@ template class CallbackServerStreamingHandler; template class CallbackBidiHandler; +template +class ServerReaderWriterBody; +class ServerReactor; +} // namespace internal + +} // namespace grpc_impl +namespace grpc { +class GenericServerContext; +class ServerInterface; + +namespace internal { +template +class ClientStreamingHandler; +template +class ServerStreamingHandler; +template +class RpcMethodHandler; template class TemplatedBidiStreamingHandler; template class ErrorMethodHandler; class Call; -class ServerReactor; } // namespace internal class ServerInterface; @@ -275,19 +279,19 @@ class ServerContext { friend class ::grpc::ServerInterface; friend class ::grpc_impl::Server; template - friend class ::grpc::ServerAsyncReader; + friend class ::grpc_impl::ServerAsyncReader; template - friend class ::grpc::ServerAsyncWriter; + friend class ::grpc_impl::ServerAsyncWriter; template - friend class ::grpc::ServerAsyncResponseWriter; + friend class ::grpc_impl::ServerAsyncResponseWriter; template - friend class ::grpc::ServerAsyncReaderWriter; + friend class ::grpc_impl::ServerAsyncReaderWriter; template - friend class ::grpc::ServerReader; + friend class ::grpc_impl::ServerReader; template - friend class ::grpc::ServerWriter; + friend class ::grpc_impl::ServerWriter; template - friend class ::grpc::internal::ServerReaderWriterBody; + friend class ::grpc_impl::internal::ServerReaderWriterBody; template friend class ::grpc::internal::RpcMethodHandler; template @@ -297,13 +301,13 @@ class ServerContext { template friend class ::grpc::internal::TemplatedBidiStreamingHandler; template - friend class ::grpc::internal::CallbackUnaryHandler; + friend class ::grpc_impl::internal::CallbackUnaryHandler; template - friend class ::grpc::internal::CallbackClientStreamingHandler; + friend class ::grpc_impl::internal::CallbackClientStreamingHandler; template - friend class ::grpc::internal::CallbackServerStreamingHandler; + friend class ::grpc_impl::internal::CallbackServerStreamingHandler; template - friend class ::grpc::internal::CallbackBidiHandler; + friend class ::grpc_impl::internal::CallbackBidiHandler; template <::grpc::StatusCode code> friend class ::grpc::internal::ErrorMethodHandler; friend class ::grpc_impl::ClientContext; @@ -317,7 +321,7 @@ class ServerContext { void BeginCompletionOp(::grpc::internal::Call* call, std::function callback, - ::grpc::internal::ServerReactor* reactor); + ::grpc_impl::internal::ServerReactor* reactor); /// Return the tag queued by BeginCompletionOp() ::grpc::internal::CompletionQueueTag* GetCompletionOpTag(); diff --git a/include/grpcpp/impl/codegen/service_type.h b/include/grpcpp/impl/codegen/service_type.h index 4109ee1b504..f13dfb99fa0 100644 --- a/include/grpcpp/impl/codegen/service_type.h +++ b/include/grpcpp/impl/codegen/service_type.h @@ -149,8 +149,9 @@ class Service { void RequestAsyncUnary(int index, ::grpc_impl::ServerContext* context, Message* request, internal::ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { + ::grpc_impl::CompletionQueue* call_cq, + ::grpc_impl::ServerCompletionQueue* notification_cq, + void* tag) { // Typecast the index to size_t for indexing into a vector // while preserving the API that existed before a compiler // warning was first seen (grpc/grpc#11664) @@ -160,8 +161,9 @@ class Service { } void RequestAsyncClientStreaming( int index, ::grpc_impl::ServerContext* context, - internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { + internal::ServerAsyncStreamingInterface* stream, + ::grpc_impl::CompletionQueue* call_cq, + ::grpc_impl::ServerCompletionQueue* notification_cq, void* tag) { size_t idx = static_cast(index); server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, notification_cq, tag); @@ -169,16 +171,18 @@ class Service { template void RequestAsyncServerStreaming( int index, ::grpc_impl::ServerContext* context, Message* request, - internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { + internal::ServerAsyncStreamingInterface* stream, + ::grpc_impl::CompletionQueue* call_cq, + ::grpc_impl::ServerCompletionQueue* notification_cq, void* tag) { size_t idx = static_cast(index); server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, notification_cq, tag, request); } void RequestAsyncBidiStreaming( int index, ::grpc_impl::ServerContext* context, - internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { + internal::ServerAsyncStreamingInterface* stream, + ::grpc_impl::CompletionQueue* call_cq, + ::grpc_impl::ServerCompletionQueue* notification_cq, void* tag) { size_t idx = static_cast(index); server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, notification_cq, tag); diff --git a/include/grpcpp/impl/codegen/sync_stream.h b/include/grpcpp/impl/codegen/sync_stream.h index 9d030a13a71..852fe6676bf 100644 --- a/include/grpcpp/impl/codegen/sync_stream.h +++ b/include/grpcpp/impl/codegen/sync_stream.h @@ -19,918 +19,81 @@ #ifndef GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H #define GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H -#include -#include -#include -#include -#include -#include -#include -#include +#include namespace grpc { namespace internal { -/// Common interface for all synchronous client side streaming. -class ClientStreamingInterface { - public: - virtual ~ClientStreamingInterface() {} - /// Block waiting until the stream finishes and a final status of the call is - /// available. - /// - /// It is appropriate to call this method when both: - /// * the calling code (client-side) has no more message to send - /// (this can be declared implicitly by calling this method, or - /// explicitly through an earlier call to WritesDone method of the - /// class in use, e.g. \a ClientWriterInterface::WritesDone or - /// \a ClientReaderWriterInterface::WritesDone). - /// * there are no more messages to be received from the server (which can - /// be known implicitly, or explicitly from an earlier call to \a - /// ReaderInterface::Read that returned "false"). - /// - /// This function will return either: - /// - when all incoming messages have been read and the server has - /// returned status. - /// - when the server has returned a non-OK status. - /// - OR when the call failed for some reason and the library generated a - /// status. - /// - /// Return values: - /// - \a Status contains the status code, message and details for the call - /// - the \a ClientContext associated with this call is updated with - /// possible trailing metadata sent from the server. - virtual Status Finish() = 0; -}; +typedef ::grpc_impl::internal::ClientStreamingInterface + ClientStreamingInterface; -/// Common interface for all synchronous server side streaming. -class ServerStreamingInterface { - public: - virtual ~ServerStreamingInterface() {} +typedef ::grpc_impl::internal::ServerStreamingInterface + ServerStreamingInterface; - /// Block to send initial metadata to client. - /// This call is optional, but if it is used, it cannot be used concurrently - /// with or after the \a Finish method. - /// - /// The initial metadata that will be sent to the client will be - /// taken from the \a ServerContext associated with the call. - virtual void SendInitialMetadata() = 0; -}; - -/// An interface that yields a sequence of messages of type \a R. template -class ReaderInterface { - public: - virtual ~ReaderInterface() {} - - /// Get an upper bound on the next message size available for reading on this - /// stream. - virtual bool NextMessageSize(uint32_t* sz) = 0; - - /// Block to read a message and parse to \a msg. Returns \a true on success. - /// This is thread-safe with respect to \a Write or \WritesDone methods on - /// the same stream. It should not be called concurrently with another \a - /// Read on the same stream as the order of delivery will not be defined. - /// - /// \param[out] msg The read message. - /// - /// \return \a false when there will be no more incoming messages, either - /// because the other side has called \a WritesDone() or the stream has failed - /// (or been cancelled). - virtual bool Read(R* msg) = 0; -}; +using ReaderInterface = ::grpc_impl::internal::ReaderInterface; -/// An interface that can be fed a sequence of messages of type \a W. template -class WriterInterface { - public: - virtual ~WriterInterface() {} +using WriterInterface = ::grpc_impl::internal::WriterInterface; - /// Block to write \a msg to the stream with WriteOptions \a options. - /// This is thread-safe with respect to \a ReaderInterface::Read - /// - /// \param msg The message to be written to the stream. - /// \param options The WriteOptions affecting the write operation. - /// - /// \return \a true on success, \a false when the stream has been closed. - virtual bool Write(const W& msg, WriteOptions options) = 0; +template +using ClientReaderFactory = ::grpc_impl::internal::ClientReaderFactory; - /// Block to write \a msg to the stream with default write options. - /// This is thread-safe with respect to \a ReaderInterface::Read - /// - /// \param msg The message to be written to the stream. - /// - /// \return \a true on success, \a false when the stream has been closed. - inline bool Write(const W& msg) { return Write(msg, WriteOptions()); } +template +using ClientWriterFactory = ::grpc_impl::internal::ClientWriterFactory; - /// Write \a msg and coalesce it with the writing of trailing metadata, using - /// WriteOptions \a options. - /// - /// For client, WriteLast is equivalent of performing Write and WritesDone in - /// a single step. \a msg and trailing metadata are coalesced and sent on wire - /// by calling this function. For server, WriteLast buffers the \a msg. - /// The writing of \a msg is held until the service handler returns, - /// where \a msg and trailing metadata are coalesced and sent on wire. - /// Note that WriteLast can only buffer \a msg up to the flow control window - /// size. If \a msg size is larger than the window size, it will be sent on - /// wire without buffering. - /// - /// \param[in] msg The message to be written to the stream. - /// \param[in] options The WriteOptions to be used to write this message. - void WriteLast(const W& msg, WriteOptions options) { - Write(msg, options.set_last_message()); - } -}; +template +using ClientReaderWriterFactory = + ::grpc_impl::internal::ClientReaderWriterFactory; } // namespace internal -/// Client-side interface for streaming reads of message of type \a R. template -class ClientReaderInterface : public internal::ClientStreamingInterface, - public internal::ReaderInterface { - public: - /// Block to wait for initial metadata from server. The received metadata - /// can only be accessed after this call returns. Should only be called before - /// the first read. Calling this method is optional, and if it is not called - /// the metadata will be available in ClientContext after the first read. - virtual void WaitForInitialMetadata() = 0; -}; +using ClientReaderInterface = ::grpc_impl::ClientReaderInterface; -namespace internal { template -class ClientReaderFactory { - public: - template - static ClientReader* Create(ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, - const W& request) { - return new ClientReader(channel, method, context, request); - } -}; -} // namespace internal - -/// Synchronous (blocking) client-side API for doing server-streaming RPCs, -/// where the stream of messages coming from the server has messages -/// of type \a R. -template -class ClientReader final : public ClientReaderInterface { - public: - /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for - /// semantics. - /// - // Side effect: - /// Once complete, the initial metadata read from - /// the server will be accessible through the \a ClientContext used to - /// construct this object. - void WaitForInitialMetadata() override { - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> - ops; - ops.RecvInitialMetadata(context_); - call_.PerformOps(&ops); - cq_.Pluck(&ops); /// status ignored - } +using ClientReader = ::grpc_impl::ClientReader; - bool NextMessageSize(uint32_t* sz) override { - *sz = call_.max_receive_message_size(); - return true; - } - - /// See the \a ReaderInterface.Read method for semantics. - /// Side effect: - /// This also receives initial metadata from the server, if not - /// already received (if initial metadata is received, it can be then - /// accessed through the \a ClientContext associated with this call). - bool Read(R* msg) override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpRecvMessage> - ops; - if (!context_->initial_metadata_received_) { - ops.RecvInitialMetadata(context_); - } - ops.RecvMessage(msg); - call_.PerformOps(&ops); - return cq_.Pluck(&ops) && ops.got_message; - } - - /// See the \a ClientStreamingInterface.Finish method for semantics. - /// - /// Side effect: - /// The \a ClientContext associated with this call is updated with - /// possible metadata received from the server. - Status Finish() override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientRecvStatus> ops; - Status status; - ops.ClientRecvStatus(context_, &status); - call_.PerformOps(&ops); - GPR_CODEGEN_ASSERT(cq_.Pluck(&ops)); - return status; - } - - private: - friend class internal::ClientReaderFactory; - ::grpc_impl::ClientContext* context_; - ::grpc_impl::CompletionQueue cq_; - ::grpc::internal::Call call_; - - /// Block to create a stream and write the initial metadata and \a request - /// out. Note that \a context will be used to fill in custom initial - /// metadata used to send to the server when starting the call. - template - ClientReader(::grpc::ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, const W& request) - : context_(context), - cq_(grpc_completion_queue_attributes{ - GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, - nullptr}), // Pluckable cq - call_(channel->CreateCall(method, context, &cq_)) { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose> - ops; - ops.SendInitialMetadata(&context->send_initial_metadata_, - context->initial_metadata_flags()); - // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(ops.SendMessagePtr(&request).ok()); - ops.ClientSendClose(); - call_.PerformOps(&ops); - cq_.Pluck(&ops); - } -}; - -/// Client-side interface for streaming writes of message type \a W. template -class ClientWriterInterface : public internal::ClientStreamingInterface, - public internal::WriterInterface { - public: - /// Half close writing from the client. (signal that the stream of messages - /// coming from the client is complete). - /// Blocks until currently-pending writes are completed. - /// Thread safe with respect to \a ReaderInterface::Read operations only - /// - /// \return Whether the writes were successful. - virtual bool WritesDone() = 0; -}; +using ClientWriterInterface = ::grpc_impl::ClientWriterInterface; -namespace internal { template -class ClientWriterFactory { - public: - template - static ClientWriter* Create(::grpc::ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, - R* response) { - return new ClientWriter(channel, method, context, response); - } -}; -} // namespace internal - -/// Synchronous (blocking) client-side API for doing client-streaming RPCs, -/// where the outgoing message stream coming from the client has messages of -/// type \a W. -template -class ClientWriter : public ClientWriterInterface { - public: - /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for - /// semantics. - /// - // Side effect: - /// Once complete, the initial metadata read from the server will be - /// accessible through the \a ClientContext used to construct this object. - void WaitForInitialMetadata() { - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> - ops; - ops.RecvInitialMetadata(context_); - call_.PerformOps(&ops); - cq_.Pluck(&ops); // status ignored - } +using ClientWriter = ::grpc_impl::ClientWriter; - /// See the WriterInterface.Write(const W& msg, WriteOptions options) method - /// for semantics. - /// - /// Side effect: - /// Also sends initial metadata if not already sent (using the - /// \a ClientContext associated with this call). - using ::grpc::internal::WriterInterface::Write; - bool Write(const W& msg, WriteOptions options) override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose> - ops; - - if (options.is_last_message()) { - options.set_buffer_hint(); - ops.ClientSendClose(); - } - if (context_->initial_metadata_corked_) { - ops.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - context_->set_initial_metadata_corked(false); - } - if (!ops.SendMessagePtr(&msg, options).ok()) { - return false; - } - - call_.PerformOps(&ops); - return cq_.Pluck(&ops); - } - - bool WritesDone() override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops; - ops.ClientSendClose(); - call_.PerformOps(&ops); - return cq_.Pluck(&ops); - } - - /// See the ClientStreamingInterface.Finish method for semantics. - /// Side effects: - /// - Also receives initial metadata if not already received. - /// - Attempts to fill in the \a response parameter passed - /// to the constructor of this instance with the response - /// message from the server. - Status Finish() override { - Status status; - if (!context_->initial_metadata_received_) { - finish_ops_.RecvInitialMetadata(context_); - } - finish_ops_.ClientRecvStatus(context_, &status); - call_.PerformOps(&finish_ops_); - GPR_CODEGEN_ASSERT(cq_.Pluck(&finish_ops_)); - return status; - } - - private: - friend class internal::ClientWriterFactory; - - /// Block to create a stream (i.e. send request headers and other initial - /// metadata to the server). Note that \a context will be used to fill - /// in custom initial metadata. \a response will be filled in with the - /// single expected response message from the server upon a successful - /// call to the \a Finish method of this instance. - template - ClientWriter(ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context, R* response) - : context_(context), - cq_(grpc_completion_queue_attributes{ - GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, - nullptr}), // Pluckable cq - call_(channel->CreateCall(method, context, &cq_)) { - finish_ops_.RecvMessage(response); - finish_ops_.AllowNoMessage(); - - if (!context_->initial_metadata_corked_) { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> - ops; - ops.SendInitialMetadata(&context->send_initial_metadata_, - context->initial_metadata_flags()); - call_.PerformOps(&ops); - cq_.Pluck(&ops); - } - } - - ::grpc_impl::ClientContext* context_; - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpGenericRecvMessage, - ::grpc::internal::CallOpClientRecvStatus> - finish_ops_; - ::grpc_impl::CompletionQueue cq_; - ::grpc::internal::Call call_; -}; - -/// Client-side interface for bi-directional streaming with -/// client-to-server stream messages of type \a W and -/// server-to-client stream messages of type \a R. template -class ClientReaderWriterInterface : public internal::ClientStreamingInterface, - public internal::WriterInterface, - public internal::ReaderInterface { - public: - /// Block to wait for initial metadata from server. The received metadata - /// can only be accessed after this call returns. Should only be called before - /// the first read. Calling this method is optional, and if it is not called - /// the metadata will be available in ClientContext after the first read. - virtual void WaitForInitialMetadata() = 0; +using ClientReaderWriterInterface = + ::grpc_impl::ClientReaderWriterInterface; - /// Half close writing from the client. (signal that the stream of messages - /// coming from the clinet is complete). - /// Blocks until currently-pending writes are completed. - /// Thread-safe with respect to \a ReaderInterface::Read - /// - /// \return Whether the writes were successful. - virtual bool WritesDone() = 0; -}; - -namespace internal { template -class ClientReaderWriterFactory { - public: - static ClientReaderWriter* Create( - ::grpc::ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context) { - return new ClientReaderWriter(channel, method, context); - } -}; -} // namespace internal - -/// Synchronous (blocking) client-side API for bi-directional streaming RPCs, -/// where the outgoing message stream coming from the client has messages of -/// type \a W, and the incoming messages stream coming from the server has -/// messages of type \a R. -template -class ClientReaderWriter final : public ClientReaderWriterInterface { - public: - /// Block waiting to read initial metadata from the server. - /// This call is optional, but if it is used, it cannot be used concurrently - /// with or after the \a Finish method. - /// - /// Once complete, the initial metadata read from the server will be - /// accessible through the \a ClientContext used to construct this object. - void WaitForInitialMetadata() override { - GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); - - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> - ops; - ops.RecvInitialMetadata(context_); - call_.PerformOps(&ops); - cq_.Pluck(&ops); // status ignored - } - - bool NextMessageSize(uint32_t* sz) override { - *sz = call_.max_receive_message_size(); - return true; - } - - /// See the \a ReaderInterface.Read method for semantics. - /// Side effect: - /// Also receives initial metadata if not already received (updates the \a - /// ClientContext associated with this call in that case). - bool Read(R* msg) override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpRecvMessage> - ops; - if (!context_->initial_metadata_received_) { - ops.RecvInitialMetadata(context_); - } - ops.RecvMessage(msg); - call_.PerformOps(&ops); - return cq_.Pluck(&ops) && ops.got_message; - } - - /// See the \a WriterInterface.Write method for semantics. - /// - /// Side effect: - /// Also sends initial metadata if not already sent (using the - /// \a ClientContext associated with this call to fill in values). - using ::grpc::internal::WriterInterface::Write; - bool Write(const W& msg, WriteOptions options) override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, - ::grpc::internal::CallOpSendMessage, - ::grpc::internal::CallOpClientSendClose> - ops; - - if (options.is_last_message()) { - options.set_buffer_hint(); - ops.ClientSendClose(); - } - if (context_->initial_metadata_corked_) { - ops.SendInitialMetadata(&context_->send_initial_metadata_, - context_->initial_metadata_flags()); - context_->set_initial_metadata_corked(false); - } - if (!ops.SendMessagePtr(&msg, options).ok()) { - return false; - } - - call_.PerformOps(&ops); - return cq_.Pluck(&ops); - } - - bool WritesDone() override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops; - ops.ClientSendClose(); - call_.PerformOps(&ops); - return cq_.Pluck(&ops); - } +using ClientReaderWriter = ::grpc_impl::ClientReaderWriter; - /// See the ClientStreamingInterface.Finish method for semantics. - /// - /// Side effect: - /// - the \a ClientContext associated with this call is updated with - /// possible trailing metadata sent from the server. - Status Finish() override { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, - ::grpc::internal::CallOpClientRecvStatus> - ops; - if (!context_->initial_metadata_received_) { - ops.RecvInitialMetadata(context_); - } - Status status; - ops.ClientRecvStatus(context_, &status); - call_.PerformOps(&ops); - GPR_CODEGEN_ASSERT(cq_.Pluck(&ops)); - return status; - } - - private: - friend class internal::ClientReaderWriterFactory; - - ::grpc_impl::ClientContext* context_; - ::grpc_impl::CompletionQueue cq_; - ::grpc::internal::Call call_; - - /// Block to create a stream and write the initial metadata and \a request - /// out. Note that \a context will be used to fill in custom initial metadata - /// used to send to the server when starting the call. - ClientReaderWriter(::grpc::ChannelInterface* channel, - const ::grpc::internal::RpcMethod& method, - ::grpc_impl::ClientContext* context) - : context_(context), - cq_(grpc_completion_queue_attributes{ - GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, - nullptr}), // Pluckable cq - call_(channel->CreateCall(method, context, &cq_)) { - if (!context_->initial_metadata_corked_) { - ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> - ops; - ops.SendInitialMetadata(&context->send_initial_metadata_, - context->initial_metadata_flags()); - call_.PerformOps(&ops); - cq_.Pluck(&ops); - } - } -}; - -/// Server-side interface for streaming reads of message of type \a R. template -class ServerReaderInterface : public internal::ServerStreamingInterface, - public internal::ReaderInterface {}; +using ServerReaderInterface = ::grpc_impl::ServerReaderInterface; -/// Synchronous (blocking) server-side API for doing client-streaming RPCs, -/// where the incoming message stream coming from the client has messages of -/// type \a R. template -class ServerReader final : public ServerReaderInterface { - public: - /// See the \a ServerStreamingInterface.SendInitialMetadata method - /// for semantics. Note that initial metadata will be affected by the - /// \a ServerContext associated with this call. - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - internal::CallOpSet ops; - ops.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ops.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - call_->PerformOps(&ops); - call_->cq()->Pluck(&ops); - } - - bool NextMessageSize(uint32_t* sz) override { - *sz = call_->max_receive_message_size(); - return true; - } +using ServerReader = ::grpc_impl::ServerReader; - bool Read(R* msg) override { - internal::CallOpSet> ops; - ops.RecvMessage(msg); - call_->PerformOps(&ops); - return call_->cq()->Pluck(&ops) && ops.got_message; - } - - private: - internal::Call* const call_; - ::grpc_impl::ServerContext* const ctx_; - - template - friend class internal::ClientStreamingHandler; - - ServerReader(internal::Call* call, ::grpc_impl::ServerContext* ctx) - : call_(call), ctx_(ctx) {} -}; - -/// Server-side interface for streaming writes of message of type \a W. template -class ServerWriterInterface : public internal::ServerStreamingInterface, - public internal::WriterInterface {}; +using ServerWriterInterface = ::grpc_impl::ServerWriterInterface; -/// Synchronous (blocking) server-side API for doing for doing a -/// server-streaming RPCs, where the outgoing message stream coming from the -/// server has messages of type \a W. template -class ServerWriter final : public ServerWriterInterface { - public: - /// See the \a ServerStreamingInterface.SendInitialMetadata method - /// for semantics. - /// Note that initial metadata will be affected by the - /// \a ServerContext associated with this call. - void SendInitialMetadata() override { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - internal::CallOpSet ops; - ops.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ops.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - call_->PerformOps(&ops); - call_->cq()->Pluck(&ops); - } +using ServerWriter = ::grpc_impl::ServerWriter; - /// See the \a WriterInterface.Write method for semantics. - /// - /// Side effect: - /// Also sends initial metadata if not already sent (using the - /// \a ClientContext associated with this call to fill in values). - using internal::WriterInterface::Write; - bool Write(const W& msg, WriteOptions options) override { - if (options.is_last_message()) { - options.set_buffer_hint(); - } - - if (!ctx_->pending_ops_.SendMessagePtr(&msg, options).ok()) { - return false; - } - if (!ctx_->sent_initial_metadata_) { - ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ctx_->pending_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - call_->PerformOps(&ctx_->pending_ops_); - // if this is the last message we defer the pluck until AFTER we start - // the trailing md op. This prevents hangs. See - // https://github.com/grpc/grpc/issues/11546 - if (options.is_last_message()) { - ctx_->has_pending_ops_ = true; - return true; - } - ctx_->has_pending_ops_ = false; - return call_->cq()->Pluck(&ctx_->pending_ops_); - } - - private: - internal::Call* const call_; - ::grpc_impl::ServerContext* const ctx_; - - template - friend class internal::ServerStreamingHandler; - - ServerWriter(internal::Call* call, ::grpc_impl::ServerContext* ctx) - : call_(call), ctx_(ctx) {} -}; - -/// Server-side interface for bi-directional streaming. template -class ServerReaderWriterInterface : public internal::ServerStreamingInterface, - public internal::WriterInterface, - public internal::ReaderInterface {}; - -/// Actual implementation of bi-directional streaming -namespace internal { -template -class ServerReaderWriterBody final { - public: - ServerReaderWriterBody(Call* call, ::grpc_impl::ServerContext* ctx) - : call_(call), ctx_(ctx) {} - - void SendInitialMetadata() { - GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); - - CallOpSet ops; - ops.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ops.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - call_->PerformOps(&ops); - call_->cq()->Pluck(&ops); - } +using ServerReaderWriterInterface = + ::grpc_impl::ServerReaderWriterInterface; - bool NextMessageSize(uint32_t* sz) { - *sz = call_->max_receive_message_size(); - return true; - } - - bool Read(R* msg) { - CallOpSet> ops; - ops.RecvMessage(msg); - call_->PerformOps(&ops); - return call_->cq()->Pluck(&ops) && ops.got_message; - } - - bool Write(const W& msg, WriteOptions options) { - if (options.is_last_message()) { - options.set_buffer_hint(); - } - if (!ctx_->pending_ops_.SendMessagePtr(&msg, options).ok()) { - return false; - } - if (!ctx_->sent_initial_metadata_) { - ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_, - ctx_->initial_metadata_flags()); - if (ctx_->compression_level_set()) { - ctx_->pending_ops_.set_compression_level(ctx_->compression_level()); - } - ctx_->sent_initial_metadata_ = true; - } - call_->PerformOps(&ctx_->pending_ops_); - // if this is the last message we defer the pluck until AFTER we start - // the trailing md op. This prevents hangs. See - // https://github.com/grpc/grpc/issues/11546 - if (options.is_last_message()) { - ctx_->has_pending_ops_ = true; - return true; - } - ctx_->has_pending_ops_ = false; - return call_->cq()->Pluck(&ctx_->pending_ops_); - } - - private: - Call* const call_; - ::grpc_impl::ServerContext* const ctx_; -}; - -} // namespace internal - -/// Synchronous (blocking) server-side API for a bidirectional -/// streaming call, where the incoming message stream coming from the client has -/// messages of type \a R, and the outgoing message streaming coming from -/// the server has messages of type \a W. template -class ServerReaderWriter final : public ServerReaderWriterInterface { - public: - /// See the \a ServerStreamingInterface.SendInitialMetadata method - /// for semantics. Note that initial metadata will be affected by the - /// \a ServerContext associated with this call. - void SendInitialMetadata() override { body_.SendInitialMetadata(); } - - bool NextMessageSize(uint32_t* sz) override { - return body_.NextMessageSize(sz); - } - - bool Read(R* msg) override { return body_.Read(msg); } - - /// See the \a WriterInterface.Write(const W& msg, WriteOptions options) - /// method for semantics. - /// Side effect: - /// Also sends initial metadata if not already sent (using the \a - /// ServerContext associated with this call). - using internal::WriterInterface::Write; - bool Write(const W& msg, WriteOptions options) override { - return body_.Write(msg, options); - } - - private: - internal::ServerReaderWriterBody body_; - - friend class internal::TemplatedBidiStreamingHandler, - false>; - ServerReaderWriter(internal::Call* call, ::grpc_impl::ServerContext* ctx) - : body_(call, ctx) {} -}; +using ServerReaderWriter = ::grpc_impl::ServerReaderWriter; -/// A class to represent a flow-controlled unary call. This is something -/// of a hybrid between conventional unary and streaming. This is invoked -/// through a unary call on the client side, but the server responds to it -/// as though it were a single-ping-pong streaming call. The server can use -/// the \a NextMessageSize method to determine an upper-bound on the size of -/// the message. A key difference relative to streaming: ServerUnaryStreamer -/// must have exactly 1 Read and exactly 1 Write, in that order, to function -/// correctly. Otherwise, the RPC is in error. template -class ServerUnaryStreamer final - : public ServerReaderWriterInterface { - public: - /// Block to send initial metadata to client. - /// Implicit input parameter: - /// - the \a ServerContext associated with this call will be used for - /// sending initial metadata. - void SendInitialMetadata() override { body_.SendInitialMetadata(); } +using ServerUnaryStreamer = + ::grpc_impl::ServerUnaryStreamer; - /// Get an upper bound on the request message size from the client. - bool NextMessageSize(uint32_t* sz) override { - return body_.NextMessageSize(sz); - } - - /// Read a message of type \a R into \a msg. Completion will be notified by \a - /// tag on the associated completion queue. - /// This is thread-safe with respect to \a Write or \a WritesDone methods. It - /// should not be called concurrently with other streaming APIs - /// on the same stream. It is not meaningful to call it concurrently - /// with another \a ReaderInterface::Read on the same stream since reads on - /// the same stream are delivered in order. - /// - /// \param[out] msg Where to eventually store the read message. - /// \param[in] tag The tag identifying the operation. - bool Read(RequestType* request) override { - if (read_done_) { - return false; - } - read_done_ = true; - return body_.Read(request); - } - - /// Block to write \a msg to the stream with WriteOptions \a options. - /// This is thread-safe with respect to \a ReaderInterface::Read - /// - /// \param msg The message to be written to the stream. - /// \param options The WriteOptions affecting the write operation. - /// - /// \return \a true on success, \a false when the stream has been closed. - using internal::WriterInterface::Write; - bool Write(const ResponseType& response, WriteOptions options) override { - if (write_done_ || !read_done_) { - return false; - } - write_done_ = true; - return body_.Write(response, options); - } - - private: - internal::ServerReaderWriterBody body_; - bool read_done_; - bool write_done_; - - friend class internal::TemplatedBidiStreamingHandler< - ServerUnaryStreamer, true>; - ServerUnaryStreamer(internal::Call* call, ::grpc_impl::ServerContext* ctx) - : body_(call, ctx), read_done_(false), write_done_(false) {} -}; - -/// A class to represent a flow-controlled server-side streaming call. -/// This is something of a hybrid between server-side and bidi streaming. -/// This is invoked through a server-side streaming call on the client side, -/// but the server responds to it as though it were a bidi streaming call that -/// must first have exactly 1 Read and then any number of Writes. template -class ServerSplitStreamer final - : public ServerReaderWriterInterface { - public: - /// Block to send initial metadata to client. - /// Implicit input parameter: - /// - the \a ServerContext associated with this call will be used for - /// sending initial metadata. - void SendInitialMetadata() override { body_.SendInitialMetadata(); } - - /// Get an upper bound on the request message size from the client. - bool NextMessageSize(uint32_t* sz) override { - return body_.NextMessageSize(sz); - } - - /// Read a message of type \a R into \a msg. Completion will be notified by \a - /// tag on the associated completion queue. - /// This is thread-safe with respect to \a Write or \a WritesDone methods. It - /// should not be called concurrently with other streaming APIs - /// on the same stream. It is not meaningful to call it concurrently - /// with another \a ReaderInterface::Read on the same stream since reads on - /// the same stream are delivered in order. - /// - /// \param[out] msg Where to eventually store the read message. - /// \param[in] tag The tag identifying the operation. - bool Read(RequestType* request) override { - if (read_done_) { - return false; - } - read_done_ = true; - return body_.Read(request); - } - - /// Block to write \a msg to the stream with WriteOptions \a options. - /// This is thread-safe with respect to \a ReaderInterface::Read - /// - /// \param msg The message to be written to the stream. - /// \param options The WriteOptions affecting the write operation. - /// - /// \return \a true on success, \a false when the stream has been closed. - using internal::WriterInterface::Write; - bool Write(const ResponseType& response, WriteOptions options) override { - return read_done_ && body_.Write(response, options); - } - - private: - internal::ServerReaderWriterBody body_; - bool read_done_; - - friend class internal::TemplatedBidiStreamingHandler< - ServerSplitStreamer, false>; - ServerSplitStreamer(internal::Call* call, ::grpc_impl::ServerContext* ctx) - : body_(call, ctx), read_done_(false) {} -}; +using ServerSplitStreamer = + ::grpc_impl::ServerSplitStreamer; } // namespace grpc diff --git a/include/grpcpp/impl/codegen/sync_stream_impl.h b/include/grpcpp/impl/codegen/sync_stream_impl.h new file mode 100644 index 00000000000..dc764e0e27b --- /dev/null +++ b/include/grpcpp/impl/codegen/sync_stream_impl.h @@ -0,0 +1,944 @@ +/* + * + * 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_SYNC_STREAM_IMPL_H +#define GRPCPP_IMPL_CODEGEN_SYNC_STREAM_IMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc_impl { + +namespace internal { +/// Common interface for all synchronous client side streaming. +class ClientStreamingInterface { + public: + virtual ~ClientStreamingInterface() {} + + /// Block waiting until the stream finishes and a final status of the call is + /// available. + /// + /// It is appropriate to call this method when both: + /// * the calling code (client-side) has no more message to send + /// (this can be declared implicitly by calling this method, or + /// explicitly through an earlier call to WritesDone method of the + /// class in use, e.g. \a ClientWriterInterface::WritesDone or + /// \a ClientReaderWriterInterface::WritesDone). + /// * there are no more messages to be received from the server (which can + /// be known implicitly, or explicitly from an earlier call to \a + /// ReaderInterface::Read that returned "false"). + /// + /// This function will return either: + /// - when all incoming messages have been read and the server has + /// returned status. + /// - when the server has returned a non-OK status. + /// - OR when the call failed for some reason and the library generated a + /// status. + /// + /// Return values: + /// - \a Status contains the status code, message and details for the call + /// - the \a ClientContext associated with this call is updated with + /// possible trailing metadata sent from the server. + virtual ::grpc::Status Finish() = 0; +}; + +/// Common interface for all synchronous server side streaming. +class ServerStreamingInterface { + public: + virtual ~ServerStreamingInterface() {} + + /// Block to send initial metadata to client. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a Finish method. + /// + /// The initial metadata that will be sent to the client will be + /// taken from the \a ServerContext associated with the call. + virtual void SendInitialMetadata() = 0; +}; + +/// An interface that yields a sequence of messages of type \a R. +template +class ReaderInterface { + public: + virtual ~ReaderInterface() {} + + /// Get an upper bound on the next message size available for reading on this + /// stream. + virtual bool NextMessageSize(uint32_t* sz) = 0; + + /// Block to read a message and parse to \a msg. Returns \a true on success. + /// This is thread-safe with respect to \a Write or \WritesDone methods on + /// the same stream. It should not be called concurrently with another \a + /// Read on the same stream as the order of delivery will not be defined. + /// + /// \param[out] msg The read message. + /// + /// \return \a false when there will be no more incoming messages, either + /// because the other side has called \a WritesDone() or the stream has failed + /// (or been cancelled). + virtual bool Read(R* msg) = 0; +}; + +/// An interface that can be fed a sequence of messages of type \a W. +template +class WriterInterface { + public: + virtual ~WriterInterface() {} + + /// Block to write \a msg to the stream with WriteOptions \a options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// \param options The WriteOptions affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + virtual bool Write(const W& msg, ::grpc::WriteOptions options) = 0; + + /// Block to write \a msg to the stream with default write options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// + /// \return \a true on success, \a false when the stream has been closed. + inline bool Write(const W& msg) { return Write(msg, ::grpc::WriteOptions()); } + + /// Write \a msg and coalesce it with the writing of trailing metadata, using + /// WriteOptions \a options. + /// + /// For client, WriteLast is equivalent of performing Write and WritesDone in + /// a single step. \a msg and trailing metadata are coalesced and sent on wire + /// by calling this function. For server, WriteLast buffers the \a msg. + /// The writing of \a msg is held until the service handler returns, + /// where \a msg and trailing metadata are coalesced and sent on wire. + /// Note that WriteLast can only buffer \a msg up to the flow control window + /// size. If \a msg size is larger than the window size, it will be sent on + /// wire without buffering. + /// + /// \param[in] msg The message to be written to the stream. + /// \param[in] options The WriteOptions to be used to write this message. + void WriteLast(const W& msg, ::grpc::WriteOptions options) { + Write(msg, options.set_last_message()); + } +}; + +} // namespace internal + +/// Client-side interface for streaming reads of message of type \a R. +template +class ClientReaderInterface : public internal::ClientStreamingInterface, + public internal::ReaderInterface { + public: + /// Block to wait for initial metadata from server. The received metadata + /// can only be accessed after this call returns. Should only be called before + /// the first read. Calling this method is optional, and if it is not called + /// the metadata will be available in ClientContext after the first read. + virtual void WaitForInitialMetadata() = 0; +}; + +namespace internal { +template +class ClientReaderFactory { + public: + template + static ClientReader* Create(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + const W& request) { + return new ClientReader(channel, method, context, request); + } +}; +} // namespace internal + +/// Synchronous (blocking) client-side API for doing server-streaming RPCs, +/// where the stream of messages coming from the server has messages +/// of type \a R. +template +class ClientReader final : public ClientReaderInterface { + public: + /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for + /// semantics. + /// + // Side effect: + /// Once complete, the initial metadata read from + /// the server will be accessible through the \a ClientContext used to + /// construct this object. + void WaitForInitialMetadata() override { + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); /// status ignored + } + + bool NextMessageSize(uint32_t* sz) override { + *sz = call_.max_receive_message_size(); + return true; + } + + /// See the \a ReaderInterface.Read method for semantics. + /// Side effect: + /// This also receives initial metadata from the server, if not + /// already received (if initial metadata is received, it can be then + /// accessed through the \a ClientContext associated with this call). + bool Read(R* msg) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ops.RecvMessage(msg); + call_.PerformOps(&ops); + return cq_.Pluck(&ops) && ops.got_message; + } + + /// See the \a ClientStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// The \a ClientContext associated with this call is updated with + /// possible metadata received from the server. + ::grpc::Status Finish() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientRecvStatus> ops; + ::grpc::Status status; + ops.ClientRecvStatus(context_, &status); + call_.PerformOps(&ops); + GPR_CODEGEN_ASSERT(cq_.Pluck(&ops)); + return status; + } + + private: + friend class internal::ClientReaderFactory; + ::grpc_impl::ClientContext* context_; + ::grpc_impl::CompletionQueue cq_; + ::grpc::internal::Call call_; + + /// Block to create a stream and write the initial metadata and \a request + /// out. Note that \a context will be used to fill in custom initial + /// metadata used to send to the server when starting the call. + template + ClientReader(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, const W& request) + : context_(context), + cq_(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}), // Pluckable cq + call_(channel->CreateCall(method, context, &cq_)) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + ops; + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(ops.SendMessagePtr(&request).ok()); + ops.ClientSendClose(); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } +}; + +/// Client-side interface for streaming writes of message type \a W. +template +class ClientWriterInterface : public internal::ClientStreamingInterface, + public internal::WriterInterface { + public: + /// Half close writing from the client. (signal that the stream of messages + /// coming from the client is complete). + /// Blocks until currently-pending writes are completed. + /// Thread safe with respect to \a ReaderInterface::Read operations only + /// + /// \return Whether the writes were successful. + virtual bool WritesDone() = 0; +}; + +namespace internal { +template +class ClientWriterFactory { + public: + template + static ClientWriter* Create(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, + R* response) { + return new ClientWriter(channel, method, context, response); + } +}; +} // namespace internal + +/// Synchronous (blocking) client-side API for doing client-streaming RPCs, +/// where the outgoing message stream coming from the client has messages of +/// type \a W. +template +class ClientWriter : public ClientWriterInterface { + public: + /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for + /// semantics. + /// + // Side effect: + /// Once complete, the initial metadata read from the server will be + /// accessible through the \a ClientContext used to construct this object. + void WaitForInitialMetadata() { + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); // status ignored + } + + /// See the WriterInterface.Write(const W& msg, WriteOptions options) method + /// for semantics. + /// + /// Side effect: + /// Also sends initial metadata if not already sent (using the + /// \a ClientContext associated with this call). + using internal::WriterInterface::Write; + bool Write(const W& msg, ::grpc::WriteOptions options) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + ops; + + if (options.is_last_message()) { + options.set_buffer_hint(); + ops.ClientSendClose(); + } + if (context_->initial_metadata_corked_) { + ops.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + context_->set_initial_metadata_corked(false); + } + if (!ops.SendMessagePtr(&msg, options).ok()) { + return false; + } + + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + bool WritesDone() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops; + ops.ClientSendClose(); + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + /// See the ClientStreamingInterface.Finish method for semantics. + /// Side effects: + /// - Also receives initial metadata if not already received. + /// - Attempts to fill in the \a response parameter passed + /// to the constructor of this instance with the response + /// message from the server. + ::grpc::Status Finish() override { + ::grpc::Status status; + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, &status); + call_.PerformOps(&finish_ops_); + GPR_CODEGEN_ASSERT(cq_.Pluck(&finish_ops_)); + return status; + } + + private: + friend class internal::ClientWriterFactory; + + /// Block to create a stream (i.e. send request headers and other initial + /// metadata to the server). Note that \a context will be used to fill + /// in custom initial metadata. \a response will be filled in with the + /// single expected response message from the server upon a successful + /// call to the \a Finish method of this instance. + template + ClientWriter(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context, R* response) + : context_(context), + cq_(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}), // Pluckable cq + call_(channel->CreateCall(method, context, &cq_)) { + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + + if (!context_->initial_metadata_corked_) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + ops; + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + } + + ::grpc_impl::ClientContext* context_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpGenericRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; + ::grpc_impl::CompletionQueue cq_; + ::grpc::internal::Call call_; +}; + +/// Client-side interface for bi-directional streaming with +/// client-to-server stream messages of type \a W and +/// server-to-client stream messages of type \a R. +template +class ClientReaderWriterInterface : public internal::ClientStreamingInterface, + public internal::WriterInterface, + public internal::ReaderInterface { + public: + /// Block to wait for initial metadata from server. The received metadata + /// can only be accessed after this call returns. Should only be called before + /// the first read. Calling this method is optional, and if it is not called + /// the metadata will be available in ClientContext after the first read. + virtual void WaitForInitialMetadata() = 0; + + /// Half close writing from the client. (signal that the stream of messages + /// coming from the clinet is complete). + /// Blocks until currently-pending writes are completed. + /// Thread-safe with respect to \a ReaderInterface::Read + /// + /// \return Whether the writes were successful. + virtual bool WritesDone() = 0; +}; + +namespace internal { +template +class ClientReaderWriterFactory { + public: + static ClientReaderWriter* Create( + ::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context) { + return new ClientReaderWriter(channel, method, context); + } +}; +} // namespace internal + +/// Synchronous (blocking) client-side API for bi-directional streaming RPCs, +/// where the outgoing message stream coming from the client has messages of +/// type \a W, and the incoming messages stream coming from the server has +/// messages of type \a R. +template +class ClientReaderWriter final : public ClientReaderWriterInterface { + public: + /// Block waiting to read initial metadata from the server. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a Finish method. + /// + /// Once complete, the initial metadata read from the server will be + /// accessible through the \a ClientContext used to construct this object. + void WaitForInitialMetadata() override { + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); // status ignored + } + + bool NextMessageSize(uint32_t* sz) override { + *sz = call_.max_receive_message_size(); + return true; + } + + /// See the \a ReaderInterface.Read method for semantics. + /// Side effect: + /// Also receives initial metadata if not already received (updates the \a + /// ClientContext associated with this call in that case). + bool Read(R* msg) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ops.RecvMessage(msg); + call_.PerformOps(&ops); + return cq_.Pluck(&ops) && ops.got_message; + } + + /// See the \a WriterInterface.Write method for semantics. + /// + /// Side effect: + /// Also sends initial metadata if not already sent (using the + /// \a ClientContext associated with this call to fill in values). + using internal::WriterInterface::Write; + bool Write(const W& msg, ::grpc::WriteOptions options) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + ops; + + if (options.is_last_message()) { + options.set_buffer_hint(); + ops.ClientSendClose(); + } + if (context_->initial_metadata_corked_) { + ops.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + context_->set_initial_metadata_corked(false); + } + if (!ops.SendMessagePtr(&msg, options).ok()) { + return false; + } + + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + bool WritesDone() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops; + ops.ClientSendClose(); + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + /// See the ClientStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible trailing metadata sent from the server. + ::grpc::Status Finish() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpClientRecvStatus> + ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ::grpc::Status status; + ops.ClientRecvStatus(context_, &status); + call_.PerformOps(&ops); + GPR_CODEGEN_ASSERT(cq_.Pluck(&ops)); + return status; + } + + private: + friend class internal::ClientReaderWriterFactory; + + ::grpc_impl::ClientContext* context_; + ::grpc_impl::CompletionQueue cq_; + ::grpc::internal::Call call_; + + /// Block to create a stream and write the initial metadata and \a request + /// out. Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + ClientReaderWriter(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ::grpc_impl::ClientContext* context) + : context_(context), + cq_(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}), // Pluckable cq + call_(channel->CreateCall(method, context, &cq_)) { + if (!context_->initial_metadata_corked_) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + ops; + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + } +}; + +/// Server-side interface for streaming reads of message of type \a R. +template +class ServerReaderInterface : public internal::ServerStreamingInterface, + public internal::ReaderInterface {}; + +/// Synchronous (blocking) server-side API for doing client-streaming RPCs, +/// where the incoming message stream coming from the client has messages of +/// type \a R. +template +class ServerReader final : public ServerReaderInterface { + public: + /// See the \a ServerStreamingInterface.SendInitialMetadata method + /// for semantics. Note that initial metadata will be affected by the + /// \a ServerContext associated with this call. + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + ops; + ops.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + bool NextMessageSize(uint32_t* sz) override { + *sz = call_->max_receive_message_size(); + return true; + } + + bool Read(R* msg) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> ops; + ops.RecvMessage(msg); + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops) && ops.got_message; + } + + private: + ::grpc::internal::Call* const call_; + ServerContext* const ctx_; + + template + friend class ::grpc::internal::ClientStreamingHandler; + + ServerReader(::grpc::internal::Call* call, ::grpc_impl::ServerContext* ctx) + : call_(call), ctx_(ctx) {} +}; + +/// Server-side interface for streaming writes of message of type \a W. +template +class ServerWriterInterface : public internal::ServerStreamingInterface, + public internal::WriterInterface {}; + +/// Synchronous (blocking) server-side API for doing for doing a +/// server-streaming RPCs, where the outgoing message stream coming from the +/// server has messages of type \a W. +template +class ServerWriter final : public ServerWriterInterface { + public: + /// See the \a ServerStreamingInterface.SendInitialMetadata method + /// for semantics. + /// Note that initial metadata will be affected by the + /// \a ServerContext associated with this call. + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + ops; + ops.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + /// See the \a WriterInterface.Write method for semantics. + /// + /// Side effect: + /// Also sends initial metadata if not already sent (using the + /// \a ClientContext associated with this call to fill in values). + using internal::WriterInterface::Write; + bool Write(const W& msg, ::grpc::WriteOptions options) override { + if (options.is_last_message()) { + options.set_buffer_hint(); + } + + if (!ctx_->pending_ops_.SendMessagePtr(&msg, options).ok()) { + return false; + } + if (!ctx_->sent_initial_metadata_) { + ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ctx_->pending_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + call_->PerformOps(&ctx_->pending_ops_); + // if this is the last message we defer the pluck until AFTER we start + // the trailing md op. This prevents hangs. See + // https://github.com/grpc/grpc/issues/11546 + if (options.is_last_message()) { + ctx_->has_pending_ops_ = true; + return true; + } + ctx_->has_pending_ops_ = false; + return call_->cq()->Pluck(&ctx_->pending_ops_); + } + + private: + ::grpc::internal::Call* const call_; + ::grpc_impl::ServerContext* const ctx_; + + template + friend class ::grpc::internal::ServerStreamingHandler; + + ServerWriter(::grpc::internal::Call* call, ::grpc_impl::ServerContext* ctx) + : call_(call), ctx_(ctx) {} +}; + +/// Server-side interface for bi-directional streaming. +template +class ServerReaderWriterInterface : public internal::ServerStreamingInterface, + public internal::WriterInterface, + public internal::ReaderInterface {}; + +/// Actual implementation of bi-directional streaming +namespace internal { +template +class ServerReaderWriterBody final { + public: + ServerReaderWriterBody(grpc::internal::Call* call, + ::grpc_impl::ServerContext* ctx) + : call_(call), ctx_(ctx) {} + + void SendInitialMetadata() { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + grpc::internal::CallOpSet ops; + ops.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + bool NextMessageSize(uint32_t* sz) { + *sz = call_->max_receive_message_size(); + return true; + } + + bool Read(R* msg) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> ops; + ops.RecvMessage(msg); + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops) && ops.got_message; + } + + bool Write(const W& msg, ::grpc::WriteOptions options) { + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->pending_ops_.SendMessagePtr(&msg, options).ok()) { + return false; + } + if (!ctx_->sent_initial_metadata_) { + ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ctx_->pending_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + call_->PerformOps(&ctx_->pending_ops_); + // if this is the last message we defer the pluck until AFTER we start + // the trailing md op. This prevents hangs. See + // https://github.com/grpc/grpc/issues/11546 + if (options.is_last_message()) { + ctx_->has_pending_ops_ = true; + return true; + } + ctx_->has_pending_ops_ = false; + return call_->cq()->Pluck(&ctx_->pending_ops_); + } + + private: + grpc::internal::Call* const call_; + ::grpc_impl::ServerContext* const ctx_; +}; + +} // namespace internal + +/// Synchronous (blocking) server-side API for a bidirectional +/// streaming call, where the incoming message stream coming from the client has +/// messages of type \a R, and the outgoing message streaming coming from +/// the server has messages of type \a W. +template +class ServerReaderWriter final : public ServerReaderWriterInterface { + public: + /// See the \a ServerStreamingInterface.SendInitialMetadata method + /// for semantics. Note that initial metadata will be affected by the + /// \a ServerContext associated with this call. + void SendInitialMetadata() override { body_.SendInitialMetadata(); } + + bool NextMessageSize(uint32_t* sz) override { + return body_.NextMessageSize(sz); + } + + bool Read(R* msg) override { return body_.Read(msg); } + + /// See the \a WriterInterface.Write(const W& msg, WriteOptions options) + /// method for semantics. + /// Side effect: + /// Also sends initial metadata if not already sent (using the \a + /// ServerContext associated with this call). + using internal::WriterInterface::Write; + bool Write(const W& msg, ::grpc::WriteOptions options) override { + return body_.Write(msg, options); + } + + private: + internal::ServerReaderWriterBody body_; + + friend class ::grpc::internal::TemplatedBidiStreamingHandler< + ServerReaderWriter, false>; + ServerReaderWriter(::grpc::internal::Call* call, + ::grpc_impl::ServerContext* ctx) + : body_(call, ctx) {} +}; + +/// A class to represent a flow-controlled unary call. This is something +/// of a hybrid between conventional unary and streaming. This is invoked +/// through a unary call on the client side, but the server responds to it +/// as though it were a single-ping-pong streaming call. The server can use +/// the \a NextMessageSize method to determine an upper-bound on the size of +/// the message. A key difference relative to streaming: ServerUnaryStreamer +/// must have exactly 1 Read and exactly 1 Write, in that order, to function +/// correctly. Otherwise, the RPC is in error. +template +class ServerUnaryStreamer final + : public ServerReaderWriterInterface { + public: + /// Block to send initial metadata to client. + /// Implicit input parameter: + /// - the \a ServerContext associated with this call will be used for + /// sending initial metadata. + void SendInitialMetadata() override { body_.SendInitialMetadata(); } + + /// Get an upper bound on the request message size from the client. + bool NextMessageSize(uint32_t* sz) override { + return body_.NextMessageSize(sz); + } + + /// Read a message of type \a R into \a msg. Completion will be notified by \a + /// tag on the associated completion queue. + /// This is thread-safe with respect to \a Write or \a WritesDone methods. It + /// should not be called concurrently with other streaming APIs + /// on the same stream. It is not meaningful to call it concurrently + /// with another \a ReaderInterface::Read on the same stream since reads on + /// the same stream are delivered in order. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + bool Read(RequestType* request) override { + if (read_done_) { + return false; + } + read_done_ = true; + return body_.Read(request); + } + + /// Block to write \a msg to the stream with WriteOptions \a options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// \param options The WriteOptions affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + using internal::WriterInterface::Write; + bool Write(const ResponseType& response, + ::grpc::WriteOptions options) override { + if (write_done_ || !read_done_) { + return false; + } + write_done_ = true; + return body_.Write(response, options); + } + + private: + internal::ServerReaderWriterBody body_; + bool read_done_; + bool write_done_; + + friend class ::grpc::internal::TemplatedBidiStreamingHandler< + ServerUnaryStreamer, true>; + ServerUnaryStreamer(::grpc::internal::Call* call, + ::grpc_impl::ServerContext* ctx) + : body_(call, ctx), read_done_(false), write_done_(false) {} +}; + +/// A class to represent a flow-controlled server-side streaming call. +/// This is something of a hybrid between server-side and bidi streaming. +/// This is invoked through a server-side streaming call on the client side, +/// but the server responds to it as though it were a bidi streaming call that +/// must first have exactly 1 Read and then any number of Writes. +template +class ServerSplitStreamer final + : public ServerReaderWriterInterface { + public: + /// Block to send initial metadata to client. + /// Implicit input parameter: + /// - the \a ServerContext associated with this call will be used for + /// sending initial metadata. + void SendInitialMetadata() override { body_.SendInitialMetadata(); } + + /// Get an upper bound on the request message size from the client. + bool NextMessageSize(uint32_t* sz) override { + return body_.NextMessageSize(sz); + } + + /// Read a message of type \a R into \a msg. Completion will be notified by \a + /// tag on the associated completion queue. + /// This is thread-safe with respect to \a Write or \a WritesDone methods. It + /// should not be called concurrently with other streaming APIs + /// on the same stream. It is not meaningful to call it concurrently + /// with another \a ReaderInterface::Read on the same stream since reads on + /// the same stream are delivered in order. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + bool Read(RequestType* request) override { + if (read_done_) { + return false; + } + read_done_ = true; + return body_.Read(request); + } + + /// Block to write \a msg to the stream with WriteOptions \a options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// \param options The WriteOptions affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + using internal::WriterInterface::Write; + bool Write(const ResponseType& response, + ::grpc::WriteOptions options) override { + return read_done_ && body_.Write(response, options); + } + + private: + internal::ServerReaderWriterBody body_; + bool read_done_; + + friend class ::grpc::internal::TemplatedBidiStreamingHandler< + ServerSplitStreamer, false>; + ServerSplitStreamer(::grpc::internal::Call* call, + ::grpc_impl::ServerContext* ctx) + : body_(call, ctx), read_done_(false) {} +}; + +} // namespace grpc_impl + +#endif // GRPCPP_IMPL_CODEGEN_SYNC_STREAM_IMPL_H diff --git a/include/grpcpp/server_builder_impl.h b/include/grpcpp/server_builder_impl.h index 9c69adc9147..7f85e807d07 100644 --- a/include/grpcpp/server_builder_impl.h +++ b/include/grpcpp/server_builder_impl.h @@ -112,10 +112,6 @@ class ServerBuilder { /// /// It can be invoked multiple times. /// - /// If port is not provided in the \a addr (e.g., "1.2.3.4:" or "1.2.3.4"), - /// the default port (i.e., https) is used. To request an ephemeral port, - /// \a addr must include 0 as the port number (e.g., "1.2.3.4:0"). - /// /// \param addr_uri The address to try to bind to the server in URI form. If /// the scheme name is omitted, "dns:///" is assumed. To bind to any address, /// please use IPv6 any, i.e., [::]:, which also accepts IPv4 diff --git a/include/grpcpp/support/async_stream_impl.h b/include/grpcpp/support/async_stream_impl.h new file mode 100644 index 00000000000..cff700d3fa9 --- /dev/null +++ b/include/grpcpp/support/async_stream_impl.h @@ -0,0 +1,24 @@ +/* + * + * 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_SUPPORT_ASYNC_STREAM_IMPL_H +#define GRPCPP_SUPPORT_ASYNC_STREAM_IMPL_H + +#include + +#endif // GRPCPP_SUPPORT_ASYNC_STREAM_IMPL_H diff --git a/include/grpcpp/support/async_unary_call_impl.h b/include/grpcpp/support/async_unary_call_impl.h new file mode 100644 index 00000000000..488fe87515a --- /dev/null +++ b/include/grpcpp/support/async_unary_call_impl.h @@ -0,0 +1,24 @@ +/* + * + * 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_SUPPORT_ASYNC_UNARY_CALL_IMPL_H +#define GRPCPP_SUPPORT_ASYNC_UNARY_CALL_IMPL_H + +#include + +#endif // GRPCPP_SUPPORT_ASYNC_UNARY_CALL_IMPL_H diff --git a/include/grpcpp/support/client_callback_impl.h b/include/grpcpp/support/client_callback_impl.h new file mode 100644 index 00000000000..ed8df412496 --- /dev/null +++ b/include/grpcpp/support/client_callback_impl.h @@ -0,0 +1,24 @@ +/* + * + * 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_SUPPORT_CLIENT_CALLBACK_IMPL_H +#define GRPCPP_SUPPORT_CLIENT_CALLBACK_IMPL_H + +#include + +#endif // GRPCPP_SUPPORT_CLIENT_CALLBACK_IMPL_H diff --git a/include/grpcpp/support/server_callback_impl.h b/include/grpcpp/support/server_callback_impl.h new file mode 100644 index 00000000000..a91c8141dd8 --- /dev/null +++ b/include/grpcpp/support/server_callback_impl.h @@ -0,0 +1,24 @@ +/* + * + * 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_SUPPORT_SERVER_CALLBACK_IMPL_H +#define GRPCPP_SUPPORT_SERVER_CALLBACK_IMPL_H + +#include + +#endif // GRPCPP_SUPPORT_SERVER_CALLBACK_IMPL_H diff --git a/include/grpcpp/support/sync_stream_impl.h b/include/grpcpp/support/sync_stream_impl.h new file mode 100644 index 00000000000..a00e425acfc --- /dev/null +++ b/include/grpcpp/support/sync_stream_impl.h @@ -0,0 +1,24 @@ +/* + * + * 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_SUPPORT_SYNC_STREAM_IMPL_H +#define GRPCPP_SUPPORT_SYNC_STREAM_IMPL_H + +#include + +#endif // GRPCPP_SUPPORT_SYNC_STREAM_IMPL_H diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index 358efe9fd79..044647d9a81 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -143,6 +143,7 @@ grpc::string GetHeaderIncludes(grpc_generator::File* file, "grpcpp/impl/codegen/async_unary_call.h", "grpcpp/impl/codegen/client_callback.h", "grpcpp/impl/codegen/client_context.h", + "grpcpp/impl/codegen/completion_queue.h", "grpcpp/impl/codegen/method_handler_impl.h", "grpcpp/impl/codegen/proto_utils.h", "grpcpp/impl/codegen/rpc_method.h", @@ -946,11 +947,12 @@ void PrintHeaderServerCallbackMethodsHelper( " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); - printer->Print(*vars, - "virtual ::grpc::experimental::ServerReadReactor< " - "$RealRequest$, $RealResponse$>* $Method$() {\n" - " return new ::grpc::internal::UnimplementedReadReactor<\n" - " $RealRequest$, $RealResponse$>;}\n"); + printer->Print( + *vars, + "virtual ::grpc::experimental::ServerReadReactor< " + "$RealRequest$, $RealResponse$>* $Method$() {\n" + " return new ::grpc_impl::internal::UnimplementedReadReactor<\n" + " $RealRequest$, $RealResponse$>;}\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, @@ -962,11 +964,12 @@ void PrintHeaderServerCallbackMethodsHelper( " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); - printer->Print(*vars, - "virtual ::grpc::experimental::ServerWriteReactor< " - "$RealRequest$, $RealResponse$>* $Method$() {\n" - " return new ::grpc::internal::UnimplementedWriteReactor<\n" - " $RealRequest$, $RealResponse$>;}\n"); + printer->Print( + *vars, + "virtual ::grpc::experimental::ServerWriteReactor< " + "$RealRequest$, $RealResponse$>* $Method$() {\n" + " return new ::grpc_impl::internal::UnimplementedWriteReactor<\n" + " $RealRequest$, $RealResponse$>;}\n"); } else if (method->BidiStreaming()) { printer->Print( *vars, @@ -978,11 +981,12 @@ void PrintHeaderServerCallbackMethodsHelper( " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); - printer->Print(*vars, - "virtual ::grpc::experimental::ServerBidiReactor< " - "$RealRequest$, $RealResponse$>* $Method$() {\n" - " return new ::grpc::internal::UnimplementedBidiReactor<\n" - " $RealRequest$, $RealResponse$>;}\n"); + printer->Print( + *vars, + "virtual ::grpc::experimental::ServerBidiReactor< " + "$RealRequest$, $RealResponse$>* $Method$() {\n" + " return new ::grpc_impl::internal::UnimplementedBidiReactor<\n" + " $RealRequest$, $RealResponse$>;}\n"); } } @@ -1010,7 +1014,7 @@ void PrintHeaderServerMethodCallback( printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" - " new ::grpc::internal::CallbackUnaryHandler< " + " new ::grpc_impl::internal::CallbackUnaryHandler< " "$RealRequest$, $RealResponse$>(\n" " [this](::grpc::ServerContext* context,\n" " const $RealRequest$* request,\n" @@ -1024,7 +1028,7 @@ void PrintHeaderServerMethodCallback( "void SetMessageAllocatorFor_$Method$(\n" " ::grpc::experimental::MessageAllocator< " "$RealRequest$, $RealResponse$>* allocator) {\n" - " static_cast<::grpc::internal::CallbackUnaryHandler< " + " static_cast<::grpc_impl::internal::CallbackUnaryHandler< " "$RealRequest$, $RealResponse$>*>(\n" " ::grpc::Service::experimental().GetHandler($Idx$))\n" " ->SetMessageAllocator(allocator);\n"); @@ -1032,21 +1036,21 @@ void PrintHeaderServerMethodCallback( printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" - " new ::grpc::internal::CallbackClientStreamingHandler< " + " new ::grpc_impl::internal::CallbackClientStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" " [this] { return this->$Method$(); }));\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" - " new ::grpc::internal::CallbackServerStreamingHandler< " + " new ::grpc_impl::internal::CallbackServerStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" " [this] { return this->$Method$(); }));\n"); } else if (method->BidiStreaming()) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" - " new ::grpc::internal::CallbackBidiHandler< " + " new ::grpc_impl::internal::CallbackBidiHandler< " "$RealRequest$, $RealResponse$>(\n" " [this] { return this->$Method$(); }));\n"); } @@ -1084,7 +1088,7 @@ void PrintHeaderServerMethodRawCallback( printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" - " new ::grpc::internal::CallbackUnaryHandler< " + " new ::grpc_impl::internal::CallbackUnaryHandler< " "$RealRequest$, $RealResponse$>(\n" " [this](::grpc::ServerContext* context,\n" " const $RealRequest$* request,\n" @@ -1098,21 +1102,21 @@ void PrintHeaderServerMethodRawCallback( printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" - " new ::grpc::internal::CallbackClientStreamingHandler< " + " new ::grpc_impl::internal::CallbackClientStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" " [this] { return this->$Method$(); }));\n"); } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" - " new ::grpc::internal::CallbackServerStreamingHandler< " + " new ::grpc_impl::internal::CallbackServerStreamingHandler< " "$RealRequest$, $RealResponse$>(\n" " [this] { return this->$Method$(); }));\n"); } else if (method->BidiStreaming()) { printer->Print( *vars, " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" - " new ::grpc::internal::CallbackBidiHandler< " + " new ::grpc_impl::internal::CallbackBidiHandler< " "$RealRequest$, $RealResponse$>(\n" " [this] { return this->$Method$(); }));\n"); } @@ -1705,7 +1709,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "const $Request$* request, $Response$* response, " "std::function f) {\n"); printer->Print(*vars, - " ::grpc::internal::CallbackUnaryCall" + " ::grpc_impl::internal::CallbackUnaryCall" "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, " "context, request, response, std::move(f));\n}\n\n"); @@ -1715,7 +1719,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "const ::grpc::ByteBuffer* request, $Response$* response, " "std::function f) {\n"); printer->Print(*vars, - " ::grpc::internal::CallbackUnaryCall" + " ::grpc_impl::internal::CallbackUnaryCall" "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, " "context, request, response, std::move(f));\n}\n\n"); @@ -1725,7 +1729,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "const $Request$* request, $Response$* response, " "::grpc::experimental::ClientUnaryReactor* reactor) {\n"); printer->Print(*vars, - " ::grpc::internal::ClientCallbackUnaryFactory::Create" + " ::grpc_impl::internal::ClientCallbackUnaryFactory::Create" "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, " "context, request, response, reactor);\n}\n\n"); @@ -1735,7 +1739,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "const ::grpc::ByteBuffer* request, $Response$* response, " "::grpc::experimental::ClientUnaryReactor* reactor) {\n"); printer->Print(*vars, - " ::grpc::internal::ClientCallbackUnaryFactory::Create" + " ::grpc_impl::internal::ClientCallbackUnaryFactory::Create" "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, " "context, request, response, reactor);\n}\n\n"); @@ -1751,7 +1755,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, printer->Print( *vars, " return " - "::grpc::internal::ClientAsyncResponseReaderFactory< $Response$>" + "::grpc_impl::internal::ClientAsyncResponseReaderFactory< $Response$>" "::Create(channel_.get(), cq, " "rpcmethod_$Method$_, " "context, request, $AsyncStart$);\n" @@ -1762,13 +1766,13 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "::grpc::ClientWriter< $Request$>* " "$ns$$Service$::Stub::$Method$Raw(" "::grpc::ClientContext* context, $Response$* response) {\n"); - printer->Print( - *vars, - " return ::grpc::internal::ClientWriterFactory< $Request$>::Create(" - "channel_.get(), " - "rpcmethod_$Method$_, " - "context, response);\n" - "}\n\n"); + printer->Print(*vars, + " return ::grpc_impl::internal::ClientWriterFactory< " + "$Request$>::Create(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, response);\n" + "}\n\n"); printer->Print( *vars, @@ -1777,7 +1781,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "$Response$* response, " "::grpc::experimental::ClientWriteReactor< $Request$>* reactor) {\n"); printer->Print(*vars, - " ::grpc::internal::ClientCallbackWriterFactory< " + " ::grpc_impl::internal::ClientCallbackWriterFactory< " "$Request$>::Create(" "stub_->channel_.get(), " "stub_->rpcmethod_$Method$_, " @@ -1796,7 +1800,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); printer->Print( *vars, - " return ::grpc::internal::ClientAsyncWriterFactory< $Request$>" + " return ::grpc_impl::internal::ClientAsyncWriterFactory< $Request$>" "::Create(channel_.get(), cq, " "rpcmethod_$Method$_, " "context, response, $AsyncStart$$AsyncCreateArgs$);\n" @@ -1808,13 +1812,13 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "::grpc::ClientReader< $Response$>* " "$ns$$Service$::Stub::$Method$Raw(" "::grpc::ClientContext* context, const $Request$& request) {\n"); - printer->Print( - *vars, - " return ::grpc::internal::ClientReaderFactory< $Response$>::Create(" - "channel_.get(), " - "rpcmethod_$Method$_, " - "context, request);\n" - "}\n\n"); + printer->Print(*vars, + " return ::grpc_impl::internal::ClientReaderFactory< " + "$Response$>::Create(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); printer->Print( *vars, @@ -1823,7 +1827,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "$Request$* request, " "::grpc::experimental::ClientReadReactor< $Response$>* reactor) {\n"); printer->Print(*vars, - " ::grpc::internal::ClientCallbackReaderFactory< " + " ::grpc_impl::internal::ClientCallbackReaderFactory< " "$Response$>::Create(" "stub_->channel_.get(), " "stub_->rpcmethod_$Method$_, " @@ -1843,7 +1847,8 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); printer->Print( *vars, - " return ::grpc::internal::ClientAsyncReaderFactory< $Response$>" + " return ::grpc_impl::internal::ClientAsyncReaderFactory< " + "$Response$>" "::Create(channel_.get(), cq, " "rpcmethod_$Method$_, " "context, request, $AsyncStart$$AsyncCreateArgs$);\n" @@ -1855,7 +1860,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "::grpc::ClientReaderWriter< $Request$, $Response$>* " "$ns$$Service$::Stub::$Method$Raw(::grpc::ClientContext* context) {\n"); printer->Print(*vars, - " return ::grpc::internal::ClientReaderWriterFactory< " + " return ::grpc_impl::internal::ClientReaderWriterFactory< " "$Request$, $Response$>::Create(" "channel_.get(), " "rpcmethod_$Method$_, " @@ -1868,13 +1873,14 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "ClientContext* context, " "::grpc::experimental::ClientBidiReactor< $Request$,$Response$>* " "reactor) {\n"); - printer->Print(*vars, - " ::grpc::internal::ClientCallbackReaderWriterFactory< " - "$Request$,$Response$>::Create(" - "stub_->channel_.get(), " - "stub_->rpcmethod_$Method$_, " - "context, reactor);\n" - "}\n\n"); + printer->Print( + *vars, + " ::grpc_impl::internal::ClientCallbackReaderWriterFactory< " + "$Request$,$Response$>::Create(" + "stub_->channel_.get(), " + "stub_->rpcmethod_$Method$_, " + "context, reactor);\n" + "}\n\n"); for (auto async_prefix : async_prefixes) { (*vars)["AsyncPrefix"] = async_prefix.prefix; @@ -1888,7 +1894,7 @@ void PrintSourceClientMethod(grpc_generator::Printer* printer, "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); printer->Print(*vars, " return " - "::grpc::internal::ClientAsyncReaderWriterFactory< " + "::grpc_impl::internal::ClientAsyncReaderWriterFactory< " "$Request$, $Response$>::Create(" "channel_.get(), cq, " "rpcmethod_$Method$_, " @@ -2279,7 +2285,8 @@ void PrintMockClientMethods(grpc_generator::Printer* printer, printer->Print( *vars, "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, " - "::grpc::ClientAsyncReaderWriterInterface<$Request$, $Response$>*" + "::grpc::ClientAsyncReaderWriterInterface<$Request$, " + "$Response$>*" "(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq" "$AsyncMethodParams$));\n"); } diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc index ff728a3dc43..c48f2321cf4 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc @@ -89,9 +89,15 @@ FakeResolver::FakeResolver(ResolverArgs args) : Resolver(args.combiner, std::move(args.result_handler)) { GRPC_CLOSURE_INIT(&reresolution_closure_, ReturnReresolutionResult, this, grpc_combiner_scheduler(combiner())); - channel_args_ = grpc_channel_args_copy(args.args); FakeResolverResponseGenerator* response_generator = FakeResolverResponseGenerator::GetFromArgs(args.args); + // Channels sharing the same subchannels may have different resolver response + // generators. If we don't remove this arg, subchannel pool will create new + // subchannels for the same address instead of reusing existing ones because + // of different values of this channel arg. + const char* args_to_remove[] = {GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR}; + channel_args_ = grpc_channel_args_copy_and_remove( + args.args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove)); if (response_generator != nullptr) { response_generator->resolver_ = this; if (response_generator->has_result_) { diff --git a/src/core/lib/gprpp/host_port.cc b/src/core/lib/gprpp/host_port.cc index 13b63eb902b..e7f0e4461e9 100644 --- a/src/core/lib/gprpp/host_port.cc +++ b/src/core/lib/gprpp/host_port.cc @@ -44,7 +44,10 @@ int JoinHostPort(UniquePtr* out, const char* host, int port) { return ret; } -bool SplitHostPort(StringView name, StringView* host, StringView* port) { +namespace { +bool DoSplitHostPort(StringView name, StringView* host, StringView* port, + bool* has_port) { + *has_port = false; if (name[0] == '[') { /* Parse a bracketed host, typically an IPv6 literal. */ const size_t rbracket = name.find(']', 1); @@ -58,6 +61,7 @@ bool SplitHostPort(StringView name, StringView* host, StringView* port) { } else if (name[rbracket + 1] == ':') { /* ]: */ *port = name.substr(rbracket + 2, name.size() - rbracket - 2); + *has_port = true; } else { /* ] */ return false; @@ -76,6 +80,7 @@ bool SplitHostPort(StringView name, StringView* host, StringView* port) { /* Exactly 1 colon. Split into host:port. */ *host = name.substr(0, colon); *port = name.substr(colon + 1, name.size() - colon - 1); + *has_port = true; } else { /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ *host = name; @@ -84,6 +89,12 @@ bool SplitHostPort(StringView name, StringView* host, StringView* port) { } return true; } +} // namespace + +bool SplitHostPort(StringView name, StringView* host, StringView* port) { + bool unused; + return DoSplitHostPort(name, host, port, &unused); +} bool SplitHostPort(StringView name, UniquePtr* host, UniquePtr* port) { @@ -91,12 +102,14 @@ bool SplitHostPort(StringView name, UniquePtr* host, GPR_DEBUG_ASSERT(port != nullptr && *port == nullptr); StringView host_view; StringView port_view; - const bool ret = SplitHostPort(name, &host_view, &port_view); + bool has_port; + const bool ret = DoSplitHostPort(name, &host_view, &port_view, &has_port); if (ret) { - // We always set the host, but port is set only when it's non-empty, - // to remain backward compatible with the old split_host_port API. + // We always set the host, but port is set only when DoSplitHostPort find a + // port in the string, to remain backward compatible with the old + // gpr_split_host_port API. *host = host_view.dup(); - if (!port_view.empty()) { + if (has_port) { *port = port_view.dup(); } } diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc index 2b69f576a3c..7eedad3f379 100644 --- a/src/core/lib/slice/slice_buffer.cc +++ b/src/core/lib/slice/slice_buffer.cc @@ -262,9 +262,9 @@ void grpc_slice_buffer_move_into(grpc_slice_buffer* src, src->length = 0; } +template static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer* src, size_t n, - grpc_slice_buffer* dst, - bool incref) { + grpc_slice_buffer* dst) { GPR_ASSERT(src->length >= n); if (src->length == n) { grpc_slice_buffer_move_into(src, dst); @@ -304,12 +304,12 @@ static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer* src, size_t n, void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n, grpc_slice_buffer* dst) { - slice_buffer_move_first_maybe_ref(src, n, dst, true); + slice_buffer_move_first_maybe_ref(src, n, dst); } void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src, size_t n, grpc_slice_buffer* dst) { - slice_buffer_move_first_maybe_ref(src, n, dst, false); + slice_buffer_move_first_maybe_ref(src, n, dst); } void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src, size_t n, diff --git a/src/cpp/client/generic_stub.cc b/src/cpp/client/generic_stub.cc index 41631c794f2..e7d3df7a497 100644 --- a/src/cpp/client/generic_stub.cc +++ b/src/cpp/client/generic_stub.cc @@ -27,11 +27,10 @@ namespace grpc_impl { namespace { std::unique_ptr CallInternal( grpc::ChannelInterface* channel, grpc::ClientContext* context, - const grpc::string& method, grpc::CompletionQueue* cq, bool start, - void* tag) { + const grpc::string& method, CompletionQueue* cq, bool start, void* tag) { return std::unique_ptr( - grpc::internal::ClientAsyncReaderWriterFactory:: + internal::ClientAsyncReaderWriterFactory:: Create(channel, cq, grpc::internal::RpcMethod( method.c_str(), grpc::internal::RpcMethod::BIDI_STREAMING), @@ -43,14 +42,14 @@ std::unique_ptr CallInternal( // begin a call to a named method std::unique_ptr GenericStub::Call( grpc::ClientContext* context, const grpc::string& method, - grpc::CompletionQueue* cq, void* tag) { + CompletionQueue* cq, void* tag) { return CallInternal(channel_.get(), context, method, cq, true, tag); } // setup a call to a named method std::unique_ptr GenericStub::PrepareCall( grpc::ClientContext* context, const grpc::string& method, - grpc::CompletionQueue* cq) { + CompletionQueue* cq) { return CallInternal(channel_.get(), context, method, cq, false, nullptr); } @@ -59,21 +58,20 @@ std::unique_ptr GenericStub::PrepareUnaryCall(grpc::ClientContext* context, const grpc::string& method, const grpc::ByteBuffer& request, - grpc::CompletionQueue* cq) { + CompletionQueue* cq) { return std::unique_ptr( - grpc::internal::ClientAsyncResponseReaderFactory< - grpc::ByteBuffer>::Create(channel_.get(), cq, - grpc::internal::RpcMethod( - method.c_str(), - grpc::internal::RpcMethod::NORMAL_RPC), - context, request, false)); + internal::ClientAsyncResponseReaderFactory::Create( + channel_.get(), cq, + grpc::internal::RpcMethod(method.c_str(), + grpc::internal::RpcMethod::NORMAL_RPC), + context, request, false)); } void GenericStub::experimental_type::UnaryCall( grpc::ClientContext* context, const grpc::string& method, const grpc::ByteBuffer* request, grpc::ByteBuffer* response, std::function on_completion) { - grpc::internal::CallbackUnaryCall( + internal::CallbackUnaryCall( stub_->channel_.get(), grpc::internal::RpcMethod(method.c_str(), grpc::internal::RpcMethod::NORMAL_RPC), @@ -82,9 +80,9 @@ void GenericStub::experimental_type::UnaryCall( void GenericStub::experimental_type::PrepareBidiStreamingCall( grpc::ClientContext* context, const grpc::string& method, - grpc::experimental::ClientBidiReactor* + experimental::ClientBidiReactor* reactor) { - grpc::internal::ClientCallbackReaderWriterFactory< + internal::ClientCallbackReaderWriterFactory< grpc::ByteBuffer, grpc::ByteBuffer>::Create(stub_->channel_.get(), grpc::internal::RpcMethod( diff --git a/src/cpp/server/async_generic_service.cc b/src/cpp/server/async_generic_service.cc index 30613768295..556447a7f40 100644 --- a/src/cpp/server/async_generic_service.cc +++ b/src/cpp/server/async_generic_service.cc @@ -24,8 +24,8 @@ namespace grpc { void AsyncGenericService::RequestCall( GenericServerContext* ctx, GenericServerAsyncReaderWriter* reader_writer, - CompletionQueue* call_cq, ServerCompletionQueue* notification_cq, - void* tag) { + ::grpc_impl::CompletionQueue* call_cq, + ::grpc_impl::ServerCompletionQueue* notification_cq, void* tag) { server_->RequestAsyncGenericCall(ctx, reader_writer, call_cq, notification_cq, tag); } diff --git a/src/cpp/server/health/default_health_check_service.h b/src/cpp/server/health/default_health_check_service.h index 4b926efdfe8..14a54d53ee0 100644 --- a/src/cpp/server/health/default_health_check_service.h +++ b/src/cpp/server/health/default_health_check_service.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc index 1ba9ae1c9dc..1d68c2bdaea 100644 --- a/src/cpp/server/server_builder.cc +++ b/src/cpp/server/server_builder.cc @@ -69,14 +69,14 @@ ServerBuilder::~ServerBuilder() { } } -std::unique_ptr ServerBuilder::AddCompletionQueue( +std::unique_ptr ServerBuilder::AddCompletionQueue( bool is_frequently_polled) { - grpc::ServerCompletionQueue* cq = new grpc::ServerCompletionQueue( + ServerCompletionQueue* cq = new ServerCompletionQueue( GRPC_CQ_NEXT, is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING, nullptr); cqs_.push_back(cq); - return std::unique_ptr(cq); + return std::unique_ptr(cq); } ServerBuilder& ServerBuilder::RegisterService(grpc::Service* service) { @@ -266,10 +266,9 @@ std::unique_ptr ServerBuilder::BuildAndStart() { // This is different from the completion queues added to the server via // ServerBuilder's AddCompletionQueue() method (those completion queues // are in 'cqs_' member variable of ServerBuilder object) - std::shared_ptr>> - sync_server_cqs( - std::make_shared< - std::vector>>()); + std::shared_ptr>> + sync_server_cqs(std::make_shared< + std::vector>>()); bool has_frequently_polled_cqs = false; for (auto it = cqs_.begin(); it != cqs_.end(); ++it) { @@ -298,7 +297,7 @@ std::unique_ptr ServerBuilder::BuildAndStart() { // Create completion queues to listen to incoming rpc requests for (int i = 0; i < sync_server_settings_.num_cqs; i++) { sync_server_cqs->emplace_back( - new grpc::ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr)); + new ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr)); } } diff --git a/src/cpp/server/server_context.cc b/src/cpp/server/server_context.cc index ba834237ae9..1e27fad3988 100644 --- a/src/cpp/server/server_context.cc +++ b/src/cpp/server/server_context.cc @@ -45,8 +45,7 @@ class ServerContext::CompletionOp final public: // initial refs: one in the server context, one in the cq // must ref the call before calling constructor and after deleting this - CompletionOp(::grpc::internal::Call* call, - ::grpc::internal::ServerReactor* reactor) + CompletionOp(::grpc::internal::Call* call, internal::ServerReactor* reactor) : call_(*call), reactor_(reactor), has_tag_(false), @@ -152,7 +151,7 @@ class ServerContext::CompletionOp final } ::grpc::internal::Call call_; - ::grpc::internal::ServerReactor* const reactor_; + internal::ServerReactor* const reactor_; bool has_tag_; void* tag_; void* core_cq_tag_; @@ -294,9 +293,9 @@ void ServerContext::Clear() { } } -void ServerContext::BeginCompletionOp( - ::grpc::internal::Call* call, std::function callback, - ::grpc::internal::ServerReactor* reactor) { +void ServerContext::BeginCompletionOp(::grpc::internal::Call* call, + std::function callback, + internal::ServerReactor* reactor) { GPR_ASSERT(!completion_op_); if (rpc_info_) { rpc_info_->Ref(); diff --git a/src/csharp/Grpc.Core.Api/AuthProperty.cs b/src/csharp/Grpc.Core.Api/AuthProperty.cs index 0907edba84d..c208cee0267 100644 --- a/src/csharp/Grpc.Core.Api/AuthProperty.cs +++ b/src/csharp/Grpc.Core.Api/AuthProperty.cs @@ -17,8 +17,6 @@ #endregion using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using Grpc.Core.Utils; @@ -33,13 +31,12 @@ namespace Grpc.Core static readonly Encoding EncodingUTF8 = System.Text.Encoding.UTF8; string name; byte[] valueBytes; - Lazy value; + string lazyValue; private AuthProperty(string name, byte[] valueBytes) { this.name = GrpcPreconditions.CheckNotNull(name); this.valueBytes = GrpcPreconditions.CheckNotNull(valueBytes); - this.value = new Lazy(() => EncodingUTF8.GetString(this.valueBytes)); } /// @@ -60,7 +57,7 @@ namespace Grpc.Core { get { - return value.Value; + return lazyValue ?? (lazyValue = EncodingUTF8.GetString(this.valueBytes)); } } diff --git a/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj b/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj index 8a32bc757df..0a958299ec8 100755 --- a/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj +++ b/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj @@ -1,4 +1,4 @@ - + @@ -11,6 +11,7 @@ https://github.com/grpc/grpc gRPC RPC HTTP/2 $(GrpcCsharpVersion) + true diff --git a/src/csharp/Grpc.Core.Api/Metadata.cs b/src/csharp/Grpc.Core.Api/Metadata.cs index 27e72fbfa8b..f7f0ae1c93a 100644 --- a/src/csharp/Grpc.Core.Api/Metadata.cs +++ b/src/csharp/Grpc.Core.Api/Metadata.cs @@ -17,8 +17,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; -using System.Text.RegularExpressions; +using Grpc.Core.Api.Utils; using Grpc.Core.Utils; @@ -345,15 +346,31 @@ namespace Grpc.Core /// Creates a binary value or ascii value metadata entry from data received from the native layer. /// We trust C core to give us well-formed data, so we don't perform any checks or defensive copying. /// - internal static Entry CreateUnsafe(string key, byte[] valueBytes) + internal static Entry CreateUnsafe(string key, IntPtr source, int length) { if (HasBinaryHeaderSuffix(key)) { - return new Entry(key, null, valueBytes); + byte[] arr; + if (length == 0) + { + arr = EmptyByteArray; + } + else + { // create a local copy in a fresh array + arr = new byte[length]; + Marshal.Copy(source, arr, 0, length); + } + return new Entry(key, null, arr); + } + else + { + string s = EncodingASCII.GetString(source, length); + return new Entry(key, s, null); } - return new Entry(key, EncodingASCII.GetString(valueBytes), null); } + static readonly byte[] EmptyByteArray = new byte[0]; + private static string NormalizeKey(string key) { GrpcPreconditions.CheckNotNull(key, "key"); diff --git a/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs b/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs new file mode 100644 index 00000000000..080fbcd5c12 --- /dev/null +++ b/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs @@ -0,0 +1,54 @@ +#region Copyright notice and license +// Copyright 2019 The 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. +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Grpc.Core.Api.Utils +{ + + internal static class EncodingExtensions + { +#if NET45 // back-fill over a method missing in NET45 + /// + /// Converts byte* pointing to an encoded byte array to a string using the provided Encoding. + /// + public static unsafe string GetString(this Encoding encoding, byte* source, int byteCount) + { + if (byteCount == 0) return ""; // most callers will have already checked, but: make sure + + // allocate a right-sized string and decode into it + int charCount = encoding.GetCharCount(source, byteCount); + string s = new string('\0', charCount); + fixed (char* cPtr = s) + { + encoding.GetChars(source, byteCount, cPtr, charCount); + } + return s; + } +#endif + /// + /// Converts IntPtr pointing to a encoded byte array to a string using the provided Encoding. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe string GetString(this Encoding encoding, IntPtr ptr, int len) + { + return len == 0 ? "" : encoding.GetString((byte*)ptr.ToPointer(), len); + } + } + +} diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 7fef2c77091..693646bea1c 100755 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -6,6 +6,7 @@ net45;netcoreapp2.1 Exe true + true diff --git a/src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs b/src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs new file mode 100644 index 00000000000..4c89b993238 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs @@ -0,0 +1,62 @@ +#region Copyright notice and license +// Copyright 2019 The 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. +#endregion + +using System.Text; +using Grpc.Core.Internal; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class WellKnownStringsTest + { + [Test] + [TestCase("", true)] + [TestCase("u", false)] + [TestCase("us", false)] + [TestCase("use", false)] + [TestCase("user", false)] + [TestCase("user-", false)] + [TestCase("user-a", false)] + [TestCase("user-ag", false)] + [TestCase("user-age", false)] + [TestCase("user-agent", true)] + [TestCase("user-agent ", false)] + [TestCase("useragent ", false)] + [TestCase("User-Agent", false)] + [TestCase("sdlkfjlskjfdlkjs;lfdksflsdfkh skjdfh sdkfhskdhf skjfhk sdhjkjh", false)] + + // test for endianness snafus (reversed in segments) + [TestCase("ega-resutn", false)] + public unsafe void TestWellKnownStrings(string input, bool expected) + { + // create a copy of the data; no cheating! + byte[] bytes = Encoding.ASCII.GetBytes(input); + fixed(byte* ptr = bytes) + { + string result = WellKnownStrings.TryIdentify(ptr, bytes.Length); + if (expected) Assert.AreEqual(input, result); + else Assert.IsNull(result); + + if (expected) + { + // try again, and check we get the same instance + string again = WellKnownStrings.TryIdentify(ptr, bytes.Length); + Assert.AreSame(result, again); + } + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs index d85d7572a63..0b5f1c76e0c 100644 --- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -111,25 +111,31 @@ namespace Grpc.Core.Tests } [Test] - public void Entry_CreateUnsafe_Ascii() + public unsafe void Entry_CreateUnsafe_Ascii() { var bytes = new byte[] { (byte)'X', (byte)'y' }; - var entry = Metadata.Entry.CreateUnsafe("abc", bytes); - Assert.IsFalse(entry.IsBinary); - Assert.AreEqual("abc", entry.Key); - Assert.AreEqual("Xy", entry.Value); - CollectionAssert.AreEqual(bytes, entry.ValueBytes); + fixed (byte* ptr = bytes) + { + var entry = Metadata.Entry.CreateUnsafe("abc", new IntPtr(ptr), bytes.Length); + Assert.IsFalse(entry.IsBinary); + Assert.AreEqual("abc", entry.Key); + Assert.AreEqual("Xy", entry.Value); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + } } [Test] - public void Entry_CreateUnsafe_Binary() + public unsafe void Entry_CreateUnsafe_Binary() { var bytes = new byte[] { 1, 2, 3 }; - var entry = Metadata.Entry.CreateUnsafe("abc-bin", bytes); - Assert.IsTrue(entry.IsBinary); - Assert.AreEqual("abc-bin", entry.Key); - Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); - CollectionAssert.AreEqual(bytes, entry.ValueBytes); + fixed (byte* ptr = bytes) + { + var entry = Metadata.Entry.CreateUnsafe("abc-bin", new IntPtr(ptr), bytes.Length); + Assert.IsTrue(entry.IsBinary); + Assert.AreEqual("abc-bin", entry.Key); + Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + } } [Test] diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index afd60e73a21..1844ce335bc 100755 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -100,6 +100,8 @@ + + diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 858d2a69605..2e637c9704b 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -21,6 +21,7 @@ using System.Text; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Core.Profiling; +using System.Buffers; namespace Grpc.Core.Internal { @@ -134,9 +135,48 @@ namespace Grpc.Core.Internal { var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendStatusFromServerCompletionCallback, callback); var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero; - var statusDetailBytes = MarshalUtils.GetBytesUTF8(status.Detail); - Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata ? 1 : 0, - optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); + + const int MaxStackAllocBytes = 256; + int maxBytes = MarshalUtils.GetMaxByteCountUTF8(status.Detail); + if (maxBytes > MaxStackAllocBytes) + { + // pay the extra to get the *actual* size; this could mean that + // it ends up fitting on the stack after all, but even if not + // it will mean that we ask for a *much* smaller buffer + maxBytes = MarshalUtils.GetByteCountUTF8(status.Detail); + } + + unsafe + { + if (maxBytes <= MaxStackAllocBytes) + { // for small status, we can encode on the stack without touching arrays + // note: if init-locals is disabled, it would be more efficient + // to just stackalloc[MaxStackAllocBytes]; but by default, since we + // expect this to be small and it needs to wipe, just use maxBytes + byte* ptr = stackalloc byte[maxBytes]; + int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes); + Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0, + optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); + } + else + { // for larger status (rare), rent a buffer from the pool and + // use that for encoding + var statusBuffer = ArrayPool.Shared.Rent(maxBytes); + try + { + fixed (byte* ptr = statusBuffer) + { + int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes); + Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0, + optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); + } + } + finally + { + ArrayPool.Shared.Return(statusBuffer); + } + } + } } } diff --git a/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs b/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs index b33cb631e26..77e4ccd356d 100644 --- a/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs +++ b/src/csharp/Grpc.Core/Internal/DefaultServerCallContext.cs @@ -38,7 +38,7 @@ namespace Grpc.Core private readonly Metadata responseTrailers; private Status status; private readonly IServerResponseStream serverResponseStream; - private readonly Lazy authContext; + private AuthContext lazyAuthContext; /// /// Creates a new instance of ServerCallContext. @@ -57,8 +57,6 @@ namespace Grpc.Core this.responseTrailers = new Metadata(); this.status = Status.DefaultSuccess; this.serverResponseStream = serverResponseStream; - // TODO(jtattermusch): avoid unnecessary allocation of factory function and the lazy object - this.authContext = new Lazy(GetAuthContextEager); } protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions options) @@ -97,7 +95,7 @@ namespace Grpc.Core set => serverResponseStream.WriteOptions = value; } - protected override AuthContext AuthContextCore => authContext.Value; + protected override AuthContext AuthContextCore => lazyAuthContext ?? (lazyAuthContext = GetAuthContextEager()); private AuthContext GetAuthContextEager() { diff --git a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs index e3e41617955..313e2b5c9d7 100644 --- a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs +++ b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs @@ -17,8 +17,9 @@ #endregion using System; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using System.Text; +using Grpc.Core.Api.Utils; namespace Grpc.Core.Internal { @@ -32,34 +33,41 @@ namespace Grpc.Core.Internal /// /// Converts IntPtr pointing to a UTF-8 encoded byte array to string. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string PtrToStringUTF8(IntPtr ptr, int len) { - if (len == 0) + return EncodingUTF8.GetString(ptr, len); + } + + /// + /// UTF-8 encodes the given string into a buffer of sufficient size + /// + public static unsafe int GetBytesUTF8(string str, byte* destination, int destinationLength) + { + int charCount = str.Length; + if (charCount == 0) return 0; + fixed (char* source = str) { - return ""; + return EncodingUTF8.GetBytes(source, charCount, destination, destinationLength); } - - // TODO(jtattermusch): once Span dependency is added, - // use Span-based API to decode the string without copying the buffer. - var bytes = new byte[len]; - Marshal.Copy(ptr, bytes, 0, len); - return EncodingUTF8.GetString(bytes); } /// - /// Returns byte array containing UTF-8 encoding of given string. + /// Returns the maximum number of bytes required to encode a given string. /// - public static byte[] GetBytesUTF8(string str) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxByteCountUTF8(string str) { - return EncodingUTF8.GetBytes(str); + return EncodingUTF8.GetMaxByteCount(str.Length); } /// - /// Get string from a UTF8 encoded byte array. + /// Returns the actual number of bytes required to encode a given string. /// - public static string GetStringUTF8(byte[] bytes) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetByteCountUTF8(string str) { - return EncodingUTF8.GetString(bytes); + return EncodingUTF8.GetByteCount(str); } } } diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs index 13fde68fe0f..3edfbfa3bfd 100644 --- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs @@ -15,8 +15,7 @@ #endregion using System; using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Grpc.Core.Profiling; +using System.Text; namespace Grpc.Core.Internal { @@ -66,12 +65,13 @@ namespace Grpc.Core.Internal var index = new UIntPtr(i); UIntPtr keyLen; IntPtr keyPtr = Native.grpcsharp_metadata_array_get_key(metadataArray, index, out keyLen); - string key = Marshal.PtrToStringAnsi(keyPtr, (int)keyLen.ToUInt32()); + int keyLen32 = checked((int)keyLen.ToUInt32()); + string key = WellKnownStrings.TryIdentify(keyPtr, keyLen32) + ?? Marshal.PtrToStringAnsi(keyPtr, keyLen32); UIntPtr valueLen; IntPtr valuePtr = Native.grpcsharp_metadata_array_get_value(metadataArray, index, out valueLen); - var bytes = new byte[valueLen.ToUInt64()]; - Marshal.Copy(valuePtr, bytes, 0, bytes.Length); - metadata.Add(Metadata.Entry.CreateUnsafe(key, bytes)); + int len32 = checked((int)valueLen.ToUInt64()); + metadata.Add(Metadata.Entry.CreateUnsafe(key, valuePtr, len32)); } return metadata; } diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs index b8a60b31c40..170fc5c5657 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs @@ -469,7 +469,7 @@ namespace Grpc.Core.Internal public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); - public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); public delegate CallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); public delegate CallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); @@ -637,7 +637,7 @@ namespace Grpc.Core.Internal public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx); [DllImport(ImportName)] - public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); [DllImport(ImportName)] public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx); @@ -933,7 +933,7 @@ namespace Grpc.Core.Internal public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx); [DllImport(ImportName)] - public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); [DllImport(ImportName)] public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx); diff --git a/src/csharp/Grpc.Core/Internal/WellKnownStrings.cs b/src/csharp/Grpc.Core/Internal/WellKnownStrings.cs new file mode 100644 index 00000000000..fa33e9a1fe4 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/WellKnownStrings.cs @@ -0,0 +1,92 @@ +#region Copyright notice and license +// Copyright 2019 The 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. +#endregion + +using System; +using System.Runtime.CompilerServices; + +namespace Grpc.Core.Internal +{ + /// + /// Utility type for identifying "well-known" strings (i.e. headers/keys etc that + /// we expect to see frequently, and don't want to allocate lots of copies of) + /// + internal static class WellKnownStrings + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong Coerce64(byte* value) + { + return *(ulong*)value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint Coerce32(byte* value) + { + return *(uint*)value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ushort Coerce16(byte* value) + { + return *(ushort*)value; + } + + + /// + /// Test whether the provided byte sequence is recognized as a well-known string; if + /// so, return a shared instance of that string; otherwise, return null + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe string TryIdentify(IntPtr source, int length) + { + return TryIdentify((byte*)source.ToPointer(), length); + } + + /// + /// Test whether the provided byte sequence is recognized as a well-known string; if + /// so, return a shared instance of that string; otherwise, return null + /// + public static unsafe string TryIdentify(byte* source, int length) + { + // note: the logic here is hard-coded to constants for optimal processing; + // refer to an ASCII/hex converter (and remember to reverse **segments** for little-endian) + if (BitConverter.IsLittleEndian) // this is a JIT intrinsic; branch removal happens on modern runtimes + { + switch (length) + { + case 0: return ""; + case 10: + switch(Coerce64(source)) + { + case 0x6567612d72657375: return Coerce16(source + 8) == 0x746e ? "user-agent" : null; + } + break; + } + } + else + { + switch (length) + { + case 0: return ""; + case 10: + switch (Coerce64(source)) + { + case 0x757365722d616765: return Coerce16(source + 8) == 0x6e74 ? "user-agent" : null; + } + break; + } + } + return null; + } + } +} diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 26d182ae53b..8705f52666f 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -334,7 +334,7 @@ namespace Grpc.Core /// /// Selects corresponding handler for given call and handles the call. /// - private async Task HandleCallAsync(ServerRpcNew newRpc, CompletionQueueSafeHandle cq, Action continuation) + private async Task HandleCallAsync(ServerRpcNew newRpc, CompletionQueueSafeHandle cq, Action continuation) { try { @@ -351,7 +351,7 @@ namespace Grpc.Core } finally { - continuation(); + continuation(this, cq); } } @@ -374,7 +374,7 @@ namespace Grpc.Core // Don't await, the continuations will run on gRPC thread pool once triggered // by cq.Next(). #pragma warning disable 4014 - HandleCallAsync(newRpc, cq, () => AllowOneRpc(cq)); + HandleCallAsync(newRpc, cq, (server, state) => server.AllowOneRpc(state)); #pragma warning restore 4014 } } diff --git a/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs b/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs index 3eddb33788a..a59afa19118 100644 --- a/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs +++ b/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs @@ -61,7 +61,7 @@ namespace Grpc.Microbenchmarks var native = NativeMethods.Get(); // nop the native-call via reflection - NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags) => { + NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags) => { completionRegistry.Extract(ctx.Handle).OnComplete(true); // drain the dictionary as we go return CallError.OK; }; diff --git a/src/csharp/tests.json b/src/csharp/tests.json index cacdb305d2e..2eb786e0983 100644 --- a/src/csharp/tests.json +++ b/src/csharp/tests.json @@ -14,6 +14,7 @@ "Grpc.Core.Internal.Tests.ReusableSliceBufferTest", "Grpc.Core.Internal.Tests.SliceTest", "Grpc.Core.Internal.Tests.TimespecTest", + "Grpc.Core.Internal.Tests.WellKnownStringsTest", "Grpc.Core.Tests.AppDomainUnloadTest", "Grpc.Core.Tests.AuthContextTest", "Grpc.Core.Tests.AuthPropertyTest", diff --git a/src/python/grpcio_tests/tests/bazel_namespace_package_hack.py b/src/python/grpcio_tests/tests/bazel_namespace_package_hack.py index c6b72c327b1..006a3deecb4 100644 --- a/src/python/grpcio_tests/tests/bazel_namespace_package_hack.py +++ b/src/python/grpcio_tests/tests/bazel_namespace_package_hack.py @@ -24,9 +24,12 @@ import sys # Analysis in depth: https://github.com/bazelbuild/rules_python/issues/55 def sys_path_to_site_dir_hack(): """Add valid sys.path item to site directory to parse the .pth files.""" + items = [] for item in sys.path: if os.path.exists(item): # The only difference between sys.path and site-directory is # whether the .pth file will be parsed or not. A site-directory # will always exist in sys.path, but not another way around. - site.addsitedir(item) + items.append(item) + for item in items: + site.addsitedir(item) diff --git a/templates/CMakeLists.txt.template b/templates/CMakeLists.txt.template index 6cf2920cd1c..87088abfc53 100644 --- a/templates/CMakeLists.txt.template +++ b/templates/CMakeLists.txt.template @@ -69,7 +69,7 @@ return 'endif()\n' %> - cmake_minimum_required(VERSION 2.8) + cmake_minimum_required(VERSION 3.5.1) set(PACKAGE_NAME "grpc") set(PACKAGE_VERSION "${settings.cpp_version}") diff --git a/templates/src/csharp/Grpc.Core/Internal/native_methods.include b/templates/src/csharp/Grpc.Core/Internal/native_methods.include index 4a31171aa27..ffcaf16bca9 100644 --- a/templates/src/csharp/Grpc.Core/Internal/native_methods.include +++ b/templates/src/csharp/Grpc.Core/Internal/native_methods.include @@ -31,7 +31,7 @@ native_method_signatures = [ 'CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)', 'CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata)', 'CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx)', - 'CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags)', + 'CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags)', 'CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx)', 'CallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx)', 'CallError grpcsharp_call_start_serverside(CallSafeHandle call, BatchContextSafeHandle ctx)', @@ -107,4 +107,4 @@ for signature in native_method_signatures: native_methods.append({'returntype': match.group(1), 'name': match.group(2), 'params': match.group(3), 'comment': match.group(4)}) return list(native_methods) -%> \ No newline at end of file +%> diff --git a/test/core/gprpp/host_port_test.cc b/test/core/gprpp/host_port_test.cc index 3b392da66e8..cfe0eddb036 100644 --- a/test/core/gprpp/host_port_test.cc +++ b/test/core/gprpp/host_port_test.cc @@ -71,7 +71,9 @@ static void test_split_host_port() { split_host_port_expect("", "", nullptr, true); split_host_port_expect("[a:b]", "a:b", nullptr, true); split_host_port_expect("1.2.3.4", "1.2.3.4", nullptr, true); + split_host_port_expect("0.0.0.0:", "0.0.0.0", "", true); split_host_port_expect("a:b:c::", "a:b:c::", nullptr, true); + split_host_port_expect("[a:b:c::]:", "a:b:c::", "", true); split_host_port_expect("[a:b]:30", "a:b", "30", true); split_host_port_expect("1.2.3.4:30", "1.2.3.4", "30", true); split_host_port_expect(":30", "", "30", true); diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden index 035955023b8..34fb5bfb53f 100644 --- a/test/cpp/codegen/compiler_test_golden +++ b/test/cpp/codegen/compiler_test_golden @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -336,7 +337,7 @@ class ServiceA final { public: ExperimentalWithCallbackMethod_MethodA1() { ::grpc::Service::experimental().MarkMethodCallback(0, - new ::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( + new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( [this](::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, @@ -346,7 +347,7 @@ class ServiceA final { } void SetMessageAllocatorFor_MethodA1( ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) { - static_cast<::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( + static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( ::grpc::Service::experimental().GetHandler(0)) ->SetMessageAllocator(allocator); } @@ -367,7 +368,7 @@ class ServiceA final { public: ExperimentalWithCallbackMethod_MethodA2() { ::grpc::Service::experimental().MarkMethodCallback(1, - new ::grpc::internal::CallbackClientStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( + new ::grpc_impl::internal::CallbackClientStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( [this] { return this->MethodA2(); })); } ~ExperimentalWithCallbackMethod_MethodA2() override { @@ -379,7 +380,7 @@ class ServiceA final { return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } virtual ::grpc::experimental::ServerReadReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA2() { - return new ::grpc::internal::UnimplementedReadReactor< + return new ::grpc_impl::internal::UnimplementedReadReactor< ::grpc::testing::Request, ::grpc::testing::Response>;} }; template @@ -389,7 +390,7 @@ class ServiceA final { public: ExperimentalWithCallbackMethod_MethodA3() { ::grpc::Service::experimental().MarkMethodCallback(2, - new ::grpc::internal::CallbackServerStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( + new ::grpc_impl::internal::CallbackServerStreamingHandler< ::grpc::testing::Request, ::grpc::testing::Response>( [this] { return this->MethodA3(); })); } ~ExperimentalWithCallbackMethod_MethodA3() override { @@ -401,7 +402,7 @@ class ServiceA final { return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } virtual ::grpc::experimental::ServerWriteReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA3() { - return new ::grpc::internal::UnimplementedWriteReactor< + return new ::grpc_impl::internal::UnimplementedWriteReactor< ::grpc::testing::Request, ::grpc::testing::Response>;} }; template @@ -411,7 +412,7 @@ class ServiceA final { public: ExperimentalWithCallbackMethod_MethodA4() { ::grpc::Service::experimental().MarkMethodCallback(3, - new ::grpc::internal::CallbackBidiHandler< ::grpc::testing::Request, ::grpc::testing::Response>( + new ::grpc_impl::internal::CallbackBidiHandler< ::grpc::testing::Request, ::grpc::testing::Response>( [this] { return this->MethodA4(); })); } ~ExperimentalWithCallbackMethod_MethodA4() override { @@ -423,7 +424,7 @@ class ServiceA final { return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } virtual ::grpc::experimental::ServerBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>* MethodA4() { - return new ::grpc::internal::UnimplementedBidiReactor< + return new ::grpc_impl::internal::UnimplementedBidiReactor< ::grpc::testing::Request, ::grpc::testing::Response>;} }; typedef ExperimentalWithCallbackMethod_MethodA1 > > > ExperimentalCallbackService; @@ -582,7 +583,7 @@ class ServiceA final { public: ExperimentalWithRawCallbackMethod_MethodA1() { ::grpc::Service::experimental().MarkMethodRawCallback(0, - new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( [this](::grpc::ServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response, @@ -607,7 +608,7 @@ class ServiceA final { public: ExperimentalWithRawCallbackMethod_MethodA2() { ::grpc::Service::experimental().MarkMethodRawCallback(1, - new ::grpc::internal::CallbackClientStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + new ::grpc_impl::internal::CallbackClientStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( [this] { return this->MethodA2(); })); } ~ExperimentalWithRawCallbackMethod_MethodA2() override { @@ -619,7 +620,7 @@ class ServiceA final { return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } virtual ::grpc::experimental::ServerReadReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA2() { - return new ::grpc::internal::UnimplementedReadReactor< + return new ::grpc_impl::internal::UnimplementedReadReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>;} }; template @@ -629,7 +630,7 @@ class ServiceA final { public: ExperimentalWithRawCallbackMethod_MethodA3() { ::grpc::Service::experimental().MarkMethodRawCallback(2, - new ::grpc::internal::CallbackServerStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + new ::grpc_impl::internal::CallbackServerStreamingHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( [this] { return this->MethodA3(); })); } ~ExperimentalWithRawCallbackMethod_MethodA3() override { @@ -641,7 +642,7 @@ class ServiceA final { return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } virtual ::grpc::experimental::ServerWriteReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA3() { - return new ::grpc::internal::UnimplementedWriteReactor< + return new ::grpc_impl::internal::UnimplementedWriteReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>;} }; template @@ -651,7 +652,7 @@ class ServiceA final { public: ExperimentalWithRawCallbackMethod_MethodA4() { ::grpc::Service::experimental().MarkMethodRawCallback(3, - new ::grpc::internal::CallbackBidiHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + new ::grpc_impl::internal::CallbackBidiHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( [this] { return this->MethodA4(); })); } ~ExperimentalWithRawCallbackMethod_MethodA4() override { @@ -663,7 +664,7 @@ class ServiceA final { return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); } virtual ::grpc::experimental::ServerBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>* MethodA4() { - return new ::grpc::internal::UnimplementedBidiReactor< + return new ::grpc_impl::internal::UnimplementedBidiReactor< ::grpc::ByteBuffer, ::grpc::ByteBuffer>;} }; template @@ -814,7 +815,7 @@ class ServiceB final { public: ExperimentalWithCallbackMethod_MethodB1() { ::grpc::Service::experimental().MarkMethodCallback(0, - new ::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( + new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>( [this](::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response, @@ -824,7 +825,7 @@ class ServiceB final { } void SetMessageAllocatorFor_MethodB1( ::grpc::experimental::MessageAllocator< ::grpc::testing::Request, ::grpc::testing::Response>* allocator) { - static_cast<::grpc::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( + static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>*>( ::grpc::Service::experimental().GetHandler(0)) ->SetMessageAllocator(allocator); } @@ -883,7 +884,7 @@ class ServiceB final { public: ExperimentalWithRawCallbackMethod_MethodB1() { ::grpc::Service::experimental().MarkMethodRawCallback(0, - new ::grpc::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( + new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( [this](::grpc::ServerContext* context, const ::grpc::ByteBuffer* request, ::grpc::ByteBuffer* response, diff --git a/test/cpp/end2end/client_lb_end2end_test.cc b/test/cpp/end2end/client_lb_end2end_test.cc index d499f136ef6..8fc0eacc2fc 100644 --- a/test/cpp/end2end/client_lb_end2end_test.cc +++ b/test/cpp/end2end/client_lb_end2end_test.cc @@ -133,6 +133,59 @@ class MyTestServiceImpl : public TestServiceImpl { std::set clients_; }; +class FakeResolverResponseGeneratorWrapper { + public: + FakeResolverResponseGeneratorWrapper() + : response_generator_(grpc_core::MakeRefCounted< + grpc_core::FakeResolverResponseGenerator>()) {} + + FakeResolverResponseGeneratorWrapper( + FakeResolverResponseGeneratorWrapper&& other) { + response_generator_ = std::move(other.response_generator_); + } + + void SetNextResolution(const std::vector& ports) { + grpc_core::ExecCtx exec_ctx; + response_generator_->SetResponse(BuildFakeResults(ports)); + } + + void SetNextResolutionUponError(const std::vector& ports) { + grpc_core::ExecCtx exec_ctx; + response_generator_->SetReresolutionResponse(BuildFakeResults(ports)); + } + + void SetFailureOnReresolution() { + grpc_core::ExecCtx exec_ctx; + response_generator_->SetFailureOnReresolution(); + } + + grpc_core::FakeResolverResponseGenerator* Get() const { + return response_generator_.get(); + } + + private: + static grpc_core::Resolver::Result BuildFakeResults( + const std::vector& ports) { + grpc_core::Resolver::Result result; + for (const int& port : ports) { + char* lb_uri_str; + gpr_asprintf(&lb_uri_str, "ipv4:127.0.0.1:%d", port); + grpc_uri* lb_uri = grpc_uri_parse(lb_uri_str, true); + GPR_ASSERT(lb_uri != nullptr); + grpc_resolved_address address; + GPR_ASSERT(grpc_parse_uri(lb_uri, &address)); + result.addresses.emplace_back(address.addr, address.len, + nullptr /* args */); + grpc_uri_destroy(lb_uri); + gpr_free(lb_uri_str); + } + return result; + } + + grpc_core::RefCountedPtr + response_generator_; +}; + class ClientLbEnd2endTest : public ::testing::Test { protected: ClientLbEnd2endTest() @@ -147,11 +200,7 @@ class ClientLbEnd2endTest : public ::testing::Test { GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1); } - void SetUp() override { - grpc_init(); - response_generator_ = - grpc_core::MakeRefCounted(); - } + void SetUp() override { grpc_init(); } void TearDown() override { for (size_t i = 0; i < servers_.size(); ++i) { @@ -186,38 +235,6 @@ class ClientLbEnd2endTest : public ::testing::Test { } } - grpc_core::Resolver::Result BuildFakeResults(const std::vector& ports) { - grpc_core::Resolver::Result result; - for (const int& port : ports) { - char* lb_uri_str; - gpr_asprintf(&lb_uri_str, "ipv4:127.0.0.1:%d", port); - grpc_uri* lb_uri = grpc_uri_parse(lb_uri_str, true); - GPR_ASSERT(lb_uri != nullptr); - grpc_resolved_address address; - GPR_ASSERT(grpc_parse_uri(lb_uri, &address)); - result.addresses.emplace_back(address.addr, address.len, - nullptr /* args */); - grpc_uri_destroy(lb_uri); - gpr_free(lb_uri_str); - } - return result; - } - - void SetNextResolution(const std::vector& ports) { - grpc_core::ExecCtx exec_ctx; - response_generator_->SetResponse(BuildFakeResults(ports)); - } - - void SetNextResolutionUponError(const std::vector& ports) { - grpc_core::ExecCtx exec_ctx; - response_generator_->SetReresolutionResponse(BuildFakeResults(ports)); - } - - void SetFailureOnReresolution() { - grpc_core::ExecCtx exec_ctx; - response_generator_->SetFailureOnReresolution(); - } - std::vector GetServersPorts(size_t start_index = 0) { std::vector ports; for (size_t i = start_index; i < servers_.size(); ++i) { @@ -226,6 +243,10 @@ class ClientLbEnd2endTest : public ::testing::Test { return ports; } + FakeResolverResponseGeneratorWrapper BuildResolverResponseGenerator() { + return FakeResolverResponseGeneratorWrapper(); + } + std::unique_ptr BuildStub( const std::shared_ptr& channel) { return grpc::testing::EchoTestService::NewStub(channel); @@ -233,12 +254,13 @@ class ClientLbEnd2endTest : public ::testing::Test { std::shared_ptr BuildChannel( const grpc::string& lb_policy_name, + const FakeResolverResponseGeneratorWrapper& response_generator, ChannelArguments args = ChannelArguments()) { if (lb_policy_name.size() > 0) { args.SetLoadBalancingPolicyName(lb_policy_name); } // else, default to pick first args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR, - response_generator_.get()); + response_generator.Get()); return ::grpc::CreateCustomChannel("fake:///", creds_, args); } @@ -401,8 +423,6 @@ class ClientLbEnd2endTest : public ::testing::Test { const grpc::string server_host_; std::unique_ptr stub_; std::vector> servers_; - grpc_core::RefCountedPtr - response_generator_; const grpc::string kRequestMessage_; std::shared_ptr creds_; }; @@ -410,7 +430,8 @@ class ClientLbEnd2endTest : public ::testing::Test { TEST_F(ClientLbEnd2endTest, ChannelStateConnectingWhenResolving) { const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel(""); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("", response_generator); auto stub = BuildStub(channel); // Initial state should be IDLE. EXPECT_EQ(channel->GetState(false /* try_to_connect */), GRPC_CHANNEL_IDLE); @@ -423,7 +444,7 @@ TEST_F(ClientLbEnd2endTest, ChannelStateConnectingWhenResolving) { EXPECT_EQ(channel->GetState(false /* try_to_connect */), GRPC_CHANNEL_CONNECTING); // Return a resolver result, which allows the connection attempt to proceed. - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); // We should eventually transition into state READY. EXPECT_TRUE(WaitForChannelReady(channel.get())); } @@ -432,9 +453,11 @@ TEST_F(ClientLbEnd2endTest, PickFirst) { // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel(""); // test that pick first is the default. + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel( + "", response_generator); // test that pick first is the default. auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); for (size_t i = 0; i < servers_.size(); ++i) { CheckRpcSendOk(stub, DEBUG_LOCATION); } @@ -454,19 +477,22 @@ TEST_F(ClientLbEnd2endTest, PickFirst) { } TEST_F(ClientLbEnd2endTest, PickFirstProcessPending) { - StartServers(1); // Single server - auto channel = BuildChannel(""); // test that pick first is the default. + StartServers(1); // Single server + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel( + "", response_generator); // test that pick first is the default. auto stub = BuildStub(channel); - SetNextResolution({servers_[0]->port_}); + response_generator.SetNextResolution({servers_[0]->port_}); WaitForServer(stub, 0, DEBUG_LOCATION); // Create a new channel and its corresponding PF LB policy, which will pick // the subchannels in READY state from the previous RPC against the same // target (even if it happened over a different channel, because subchannels // are globally reused). Progress should happen without any transition from // this READY state. - auto second_channel = BuildChannel(""); + auto second_response_generator = BuildResolverResponseGenerator(); + auto second_channel = BuildChannel("", second_response_generator); auto second_stub = BuildStub(second_channel); - SetNextResolution({servers_[0]->port_}); + second_response_generator.SetNextResolution({servers_[0]->port_}); CheckRpcSendOk(second_stub, DEBUG_LOCATION); } @@ -479,16 +505,18 @@ TEST_F(ClientLbEnd2endTest, PickFirstSelectsReadyAtStartup) { grpc_pick_unused_port_or_die()}; CreateServers(2, ports); StartServer(1); - auto channel1 = BuildChannel("pick_first", args); + auto response_generator1 = BuildResolverResponseGenerator(); + auto channel1 = BuildChannel("pick_first", response_generator1, args); auto stub1 = BuildStub(channel1); - SetNextResolution(ports); + response_generator1.SetNextResolution(ports); // Wait for second server to be ready. WaitForServer(stub1, 1, DEBUG_LOCATION); // Create a second channel with the same addresses. Its PF instance // should immediately pick the second subchannel, since it's already // in READY state. - auto channel2 = BuildChannel("pick_first", args); - SetNextResolution(ports); + auto response_generator2 = BuildResolverResponseGenerator(); + auto channel2 = BuildChannel("pick_first", response_generator2, args); + response_generator2.SetNextResolution(ports); // Check that the channel reports READY without waiting for the // initial backoff. EXPECT_TRUE(WaitForChannelReady(channel2.get(), 1 /* timeout_seconds */)); @@ -500,9 +528,10 @@ TEST_F(ClientLbEnd2endTest, PickFirstBackOffInitialReconnect) { args.SetInt(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, kInitialBackOffMs); const std::vector ports = {grpc_pick_unused_port_or_die()}; const gpr_timespec t0 = gpr_now(GPR_CLOCK_MONOTONIC); - auto channel = BuildChannel("pick_first", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); // The channel won't become connected (there's no server). ASSERT_FALSE(channel->WaitForConnected( grpc_timeout_milliseconds_to_deadline(kInitialBackOffMs * 2))); @@ -529,9 +558,10 @@ TEST_F(ClientLbEnd2endTest, PickFirstBackOffMinReconnect) { constexpr int kMinReconnectBackOffMs = 1000; args.SetInt(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, kMinReconnectBackOffMs); const std::vector ports = {grpc_pick_unused_port_or_die()}; - auto channel = BuildChannel("pick_first", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); // Make connection delay a 10% longer than it's willing to in order to make // sure we are hitting the codepath that waits for the min reconnect backoff. gpr_atm_rel_store(&g_connection_delay_ms, kMinReconnectBackOffMs * 1.10); @@ -554,9 +584,10 @@ TEST_F(ClientLbEnd2endTest, PickFirstResetConnectionBackoff) { constexpr int kInitialBackOffMs = 1000; args.SetInt(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, kInitialBackOffMs); const std::vector ports = {grpc_pick_unused_port_or_die()}; - auto channel = BuildChannel("pick_first", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); // The channel won't become connected (there's no server). EXPECT_FALSE( channel->WaitForConnected(grpc_timeout_milliseconds_to_deadline(10))); @@ -585,9 +616,10 @@ TEST_F(ClientLbEnd2endTest, constexpr int kInitialBackOffMs = 1000; args.SetInt(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, kInitialBackOffMs); const std::vector ports = {grpc_pick_unused_port_or_die()}; - auto channel = BuildChannel("pick_first", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); // Wait for connect, which should fail ~immediately, because the server // is not up. gpr_log(GPR_INFO, "=== INITIAL CONNECTION ATTEMPT"); @@ -628,21 +660,22 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdates) { // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator); auto stub = BuildStub(channel); std::vector ports; // Perform one RPC against the first server. ports.emplace_back(servers_[0]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** SET [0] *******"); CheckRpcSendOk(stub, DEBUG_LOCATION); EXPECT_EQ(servers_[0]->service_.request_count(), 1); // An empty update will result in the channel going into TRANSIENT_FAILURE. ports.clear(); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** SET none *******"); grpc_connectivity_state channel_state; do { @@ -654,7 +687,7 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdates) { // Next update introduces servers_[1], making the channel recover. ports.clear(); ports.emplace_back(servers_[1]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** SET [1] *******"); WaitForServer(stub, 1, DEBUG_LOCATION); EXPECT_EQ(servers_[0]->service_.request_count(), 0); @@ -662,7 +695,7 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdates) { // And again for servers_[2] ports.clear(); ports.emplace_back(servers_[2]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** SET [2] *******"); WaitForServer(stub, 2, DEBUG_LOCATION); EXPECT_EQ(servers_[0]->service_.request_count(), 0); @@ -676,14 +709,15 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdateSuperset) { // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator); auto stub = BuildStub(channel); std::vector ports; // Perform one RPC against the first server. ports.emplace_back(servers_[0]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** SET [0] *******"); CheckRpcSendOk(stub, DEBUG_LOCATION); EXPECT_EQ(servers_[0]->service_.request_count(), 1); @@ -693,7 +727,7 @@ TEST_F(ClientLbEnd2endTest, PickFirstUpdateSuperset) { ports.clear(); ports.emplace_back(servers_[1]->port_); ports.emplace_back(servers_[0]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** SET superset *******"); CheckRpcSendOk(stub, DEBUG_LOCATION); // We stick to the previously connected server. @@ -710,12 +744,14 @@ TEST_F(ClientLbEnd2endTest, PickFirstGlobalSubchannelPool) { StartServers(kNumServers); std::vector ports = GetServersPorts(); // Create two channels that (by default) use the global subchannel pool. - auto channel1 = BuildChannel("pick_first"); + auto response_generator1 = BuildResolverResponseGenerator(); + auto channel1 = BuildChannel("pick_first", response_generator1); auto stub1 = BuildStub(channel1); - SetNextResolution(ports); - auto channel2 = BuildChannel("pick_first"); + response_generator1.SetNextResolution(ports); + auto response_generator2 = BuildResolverResponseGenerator(); + auto channel2 = BuildChannel("pick_first", response_generator2); auto stub2 = BuildStub(channel2); - SetNextResolution(ports); + response_generator2.SetNextResolution(ports); WaitForServer(stub1, 0, DEBUG_LOCATION); // Send one RPC on each channel. CheckRpcSendOk(stub1, DEBUG_LOCATION); @@ -735,12 +771,14 @@ TEST_F(ClientLbEnd2endTest, PickFirstLocalSubchannelPool) { // Create two channels that use local subchannel pool. ChannelArguments args; args.SetInt(GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL, 1); - auto channel1 = BuildChannel("pick_first", args); + auto response_generator1 = BuildResolverResponseGenerator(); + auto channel1 = BuildChannel("pick_first", response_generator1, args); auto stub1 = BuildStub(channel1); - SetNextResolution(ports); - auto channel2 = BuildChannel("pick_first", args); + response_generator1.SetNextResolution(ports); + auto response_generator2 = BuildResolverResponseGenerator(); + auto channel2 = BuildChannel("pick_first", response_generator2, args); auto stub2 = BuildStub(channel2); - SetNextResolution(ports); + response_generator2.SetNextResolution(ports); WaitForServer(stub1, 0, DEBUG_LOCATION); // Send one RPC on each channel. CheckRpcSendOk(stub1, DEBUG_LOCATION); @@ -756,13 +794,14 @@ TEST_F(ClientLbEnd2endTest, PickFirstManyUpdates) { const int kNumUpdates = 1000; const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator); auto stub = BuildStub(channel); std::vector ports = GetServersPorts(); for (size_t i = 0; i < kNumUpdates; ++i) { std::shuffle(ports.begin(), ports.end(), std::mt19937(std::random_device()())); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); // We should re-enter core at the end of the loop to give the resolution // setting closure a chance to run. if ((i + 1) % 10 == 0) CheckRpcSendOk(stub, DEBUG_LOCATION); @@ -784,16 +823,17 @@ TEST_F(ClientLbEnd2endTest, PickFirstReresolutionNoSelected) { dead_ports.emplace_back(grpc_pick_unused_port_or_die()); } } - auto channel = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator); auto stub = BuildStub(channel); // The initial resolution only contains dead ports. There won't be any // selected subchannel. Re-resolution will return the same result. - SetNextResolution(dead_ports); + response_generator.SetNextResolution(dead_ports); gpr_log(GPR_INFO, "****** INITIAL RESOLUTION SET *******"); for (size_t i = 0; i < 10; ++i) CheckRpcSendFailure(stub); // Set a re-resolution result that contains reachable ports, so that the // pick_first LB policy can recover soon. - SetNextResolutionUponError(alive_ports); + response_generator.SetNextResolutionUponError(alive_ports); gpr_log(GPR_INFO, "****** RE-RESOLUTION SET *******"); WaitForServer(stub, 0, DEBUG_LOCATION, true /* ignore_failure */); CheckRpcSendOk(stub, DEBUG_LOCATION); @@ -805,9 +845,10 @@ TEST_F(ClientLbEnd2endTest, PickFirstReresolutionNoSelected) { TEST_F(ClientLbEnd2endTest, PickFirstReconnectWithoutNewResolverResult) { std::vector ports = {grpc_pick_unused_port_or_die()}; StartServers(1, ports); - auto channel = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** INITIAL CONNECTION *******"); WaitForServer(stub, 0, DEBUG_LOCATION); gpr_log(GPR_INFO, "****** STOPPING SERVER ******"); @@ -824,9 +865,10 @@ TEST_F(ClientLbEnd2endTest, grpc_pick_unused_port_or_die()}; CreateServers(2, ports); StartServer(1); - auto channel = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("pick_first", response_generator); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** INITIAL CONNECTION *******"); WaitForServer(stub, 1, DEBUG_LOCATION); gpr_log(GPR_INFO, "****** STOPPING SERVER ******"); @@ -840,9 +882,10 @@ TEST_F(ClientLbEnd2endTest, TEST_F(ClientLbEnd2endTest, PickFirstCheckStateBeforeStartWatch) { std::vector ports = {grpc_pick_unused_port_or_die()}; StartServers(1, ports); - auto channel_1 = BuildChannel("pick_first"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel_1 = BuildChannel("pick_first", response_generator); auto stub_1 = BuildStub(channel_1); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); gpr_log(GPR_INFO, "****** RESOLUTION SET FOR CHANNEL 1 *******"); WaitForServer(stub_1, 0, DEBUG_LOCATION); gpr_log(GPR_INFO, "****** CHANNEL 1 CONNECTED *******"); @@ -851,13 +894,10 @@ TEST_F(ClientLbEnd2endTest, PickFirstCheckStateBeforeStartWatch) { // create a new subchannel and hold a ref to it. StartServers(1, ports); gpr_log(GPR_INFO, "****** SERVER RESTARTED *******"); - auto channel_2 = BuildChannel("pick_first"); + auto response_generator_2 = BuildResolverResponseGenerator(); + auto channel_2 = BuildChannel("pick_first", response_generator_2); auto stub_2 = BuildStub(channel_2); - // TODO(juanlishen): This resolution result will only be visible to channel 2 - // since the response generator is only associated with channel 2 now. We - // should change the response generator to be able to deliver updates to - // multiple channels at once. - SetNextResolution(ports); + response_generator_2.SetNextResolution(ports); gpr_log(GPR_INFO, "****** RESOLUTION SET FOR CHANNEL 2 *******"); WaitForServer(stub_2, 0, DEBUG_LOCATION, true); gpr_log(GPR_INFO, "****** CHANNEL 2 CONNECTED *******"); @@ -883,13 +923,15 @@ TEST_F(ClientLbEnd2endTest, PickFirstIdleOnDisconnect) { // Start server, send RPC, and make sure channel is READY. const int kNumServers = 1; StartServers(kNumServers); - auto channel = BuildChannel(""); // pick_first is the default. + auto response_generator = BuildResolverResponseGenerator(); + auto channel = + BuildChannel("", response_generator); // pick_first is the default. auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); CheckRpcSendOk(stub, DEBUG_LOCATION); EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY); // Stop server. Channel should go into state IDLE. - SetFailureOnReresolution(); + response_generator.SetFailureOnReresolution(); servers_[0]->Shutdown(); EXPECT_TRUE(WaitForChannelNotReady(channel.get())); EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_IDLE); @@ -897,14 +939,16 @@ TEST_F(ClientLbEnd2endTest, PickFirstIdleOnDisconnect) { } TEST_F(ClientLbEnd2endTest, PickFirstPendingUpdateAndSelectedSubchannelFails) { - auto channel = BuildChannel(""); // pick_first is the default. + auto response_generator = BuildResolverResponseGenerator(); + auto channel = + BuildChannel("", response_generator); // pick_first is the default. auto stub = BuildStub(channel); // Create a number of servers, but only start 1 of them. CreateServers(10); StartServer(0); // Initially resolve to first server and make sure it connects. gpr_log(GPR_INFO, "Phase 1: Connect to first server."); - SetNextResolution({servers_[0]->port_}); + response_generator.SetNextResolution({servers_[0]->port_}); CheckRpcSendOk(stub, DEBUG_LOCATION, true /* wait_for_ready */); EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY); // Send a resolution update with the remaining servers, none of which are @@ -916,7 +960,7 @@ TEST_F(ClientLbEnd2endTest, PickFirstPendingUpdateAndSelectedSubchannelFails) { gpr_log(GPR_INFO, "Phase 2: Resolver update pointing to remaining " "(not started) servers."); - SetNextResolution(GetServersPorts(1 /* start_index */)); + response_generator.SetNextResolution(GetServersPorts(1 /* start_index */)); // RPCs will continue to be sent to the first server. CheckRpcSendOk(stub, DEBUG_LOCATION); // Now stop the first server, so that the current subchannel list @@ -947,9 +991,11 @@ TEST_F(ClientLbEnd2endTest, PickFirstStaysIdleUponEmptyUpdate) { // Start server, send RPC, and make sure channel is READY. const int kNumServers = 1; StartServers(kNumServers); - auto channel = BuildChannel(""); // pick_first is the default. + auto response_generator = BuildResolverResponseGenerator(); + auto channel = + BuildChannel("", response_generator); // pick_first is the default. auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); CheckRpcSendOk(stub, DEBUG_LOCATION); EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY); // Stop server. Channel should go into state IDLE. @@ -958,13 +1004,13 @@ TEST_F(ClientLbEnd2endTest, PickFirstStaysIdleUponEmptyUpdate) { EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_IDLE); // Now send resolver update that includes no addresses. Channel // should stay in state IDLE. - SetNextResolution({}); + response_generator.SetNextResolution({}); EXPECT_FALSE(channel->WaitForStateChange( GRPC_CHANNEL_IDLE, grpc_timeout_seconds_to_deadline(3))); // Now bring the backend back up and send a non-empty resolver update, // and then try to send an RPC. Channel should go back into state READY. StartServer(0); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); CheckRpcSendOk(stub, DEBUG_LOCATION); EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY); } @@ -973,9 +1019,10 @@ TEST_F(ClientLbEnd2endTest, RoundRobin) { // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); // Wait until all backends are ready. do { CheckRpcSendOk(stub, DEBUG_LOCATION); @@ -999,18 +1046,20 @@ TEST_F(ClientLbEnd2endTest, RoundRobin) { TEST_F(ClientLbEnd2endTest, RoundRobinProcessPending) { StartServers(1); // Single server - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); - SetNextResolution({servers_[0]->port_}); + response_generator.SetNextResolution({servers_[0]->port_}); WaitForServer(stub, 0, DEBUG_LOCATION); // Create a new channel and its corresponding RR LB policy, which will pick // the subchannels in READY state from the previous RPC against the same // target (even if it happened over a different channel, because subchannels // are globally reused). Progress should happen without any transition from // this READY state. - auto second_channel = BuildChannel("round_robin"); + auto second_response_generator = BuildResolverResponseGenerator(); + auto second_channel = BuildChannel("round_robin", second_response_generator); auto second_stub = BuildStub(second_channel); - SetNextResolution({servers_[0]->port_}); + second_response_generator.SetNextResolution({servers_[0]->port_}); CheckRpcSendOk(second_stub, DEBUG_LOCATION); } @@ -1018,13 +1067,14 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); std::vector ports; // Start with a single server. gpr_log(GPR_INFO, "*** FIRST BACKEND ***"); ports.emplace_back(servers_[0]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); WaitForServer(stub, 0, DEBUG_LOCATION); // Send RPCs. They should all go servers_[0] for (size_t i = 0; i < 10; ++i) CheckRpcSendOk(stub, DEBUG_LOCATION); @@ -1036,7 +1086,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { gpr_log(GPR_INFO, "*** SECOND BACKEND ***"); ports.clear(); ports.emplace_back(servers_[1]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); // Wait until update has been processed, as signaled by the second backend // receiving a request. EXPECT_EQ(0, servers_[1]->service_.request_count()); @@ -1050,7 +1100,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { gpr_log(GPR_INFO, "*** THIRD BACKEND ***"); ports.clear(); ports.emplace_back(servers_[2]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); WaitForServer(stub, 2, DEBUG_LOCATION); for (size_t i = 0; i < 10; ++i) CheckRpcSendOk(stub, DEBUG_LOCATION); EXPECT_EQ(0, servers_[0]->service_.request_count()); @@ -1063,7 +1113,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { ports.emplace_back(servers_[0]->port_); ports.emplace_back(servers_[1]->port_); ports.emplace_back(servers_[2]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); WaitForServer(stub, 0, DEBUG_LOCATION); WaitForServer(stub, 1, DEBUG_LOCATION); WaitForServer(stub, 2, DEBUG_LOCATION); @@ -1075,7 +1125,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { // An empty update will result in the channel going into TRANSIENT_FAILURE. gpr_log(GPR_INFO, "*** NO BACKENDS ***"); ports.clear(); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); grpc_connectivity_state channel_state; do { channel_state = channel->GetState(true /* try to connect */); @@ -1086,7 +1136,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { gpr_log(GPR_INFO, "*** BACK TO SECOND BACKEND ***"); ports.clear(); ports.emplace_back(servers_[1]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); WaitForServer(stub, 1, DEBUG_LOCATION); channel_state = channel->GetState(false /* try to connect */); ASSERT_EQ(channel_state, GRPC_CHANNEL_READY); @@ -1097,13 +1147,14 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdates) { TEST_F(ClientLbEnd2endTest, RoundRobinUpdateInError) { const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); std::vector ports; // Start with a single server. ports.emplace_back(servers_[0]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); WaitForServer(stub, 0, DEBUG_LOCATION); // Send RPCs. They should all go to servers_[0] for (size_t i = 0; i < 10; ++i) SendRpc(stub); @@ -1116,7 +1167,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinUpdateInError) { servers_[1]->Shutdown(); ports.emplace_back(servers_[1]->port_); ports.emplace_back(servers_[2]->port_); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); WaitForServer(stub, 0, DEBUG_LOCATION); WaitForServer(stub, 2, DEBUG_LOCATION); @@ -1130,13 +1181,14 @@ TEST_F(ClientLbEnd2endTest, RoundRobinManyUpdates) { // Start servers and send one RPC per server. const int kNumServers = 3; StartServers(kNumServers); - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); std::vector ports = GetServersPorts(); for (size_t i = 0; i < 1000; ++i) { std::shuffle(ports.begin(), ports.end(), std::mt19937(std::random_device()())); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); if (i % 10 == 0) CheckRpcSendOk(stub, DEBUG_LOCATION); } // Check LB policy name for the channel. @@ -1162,9 +1214,10 @@ TEST_F(ClientLbEnd2endTest, RoundRobinReresolve) { second_ports.push_back(grpc_pick_unused_port_or_die()); } StartServers(kNumServers, first_ports); - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); - SetNextResolution(first_ports); + response_generator.SetNextResolution(first_ports); // Send a number of RPCs, which succeed. for (size_t i = 0; i < 100; ++i) { CheckRpcSendOk(stub, DEBUG_LOCATION); @@ -1188,7 +1241,7 @@ TEST_F(ClientLbEnd2endTest, RoundRobinReresolve) { StartServers(kNumServers, second_ports); // Don't notify of the update. Wait for the LB policy's re-resolution to // "pull" the new ports. - SetNextResolutionUponError(second_ports); + response_generator.SetNextResolutionUponError(second_ports); gpr_log(GPR_INFO, "****** SERVERS RESTARTED *******"); gpr_log(GPR_INFO, "****** SENDING REQUEST TO SUCCEED *******"); // Client request should eventually (but still fairly soon) succeed. @@ -1205,9 +1258,10 @@ TEST_F(ClientLbEnd2endTest, RoundRobinSingleReconnect) { const int kNumServers = 3; StartServers(kNumServers); const auto ports = GetServersPorts(); - auto channel = BuildChannel("round_robin"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator); auto stub = BuildStub(channel); - SetNextResolution(ports); + response_generator.SetNextResolution(ports); for (size_t i = 0; i < kNumServers; ++i) { WaitForServer(stub, i, DEBUG_LOCATION); } @@ -1251,9 +1305,10 @@ TEST_F(ClientLbEnd2endTest, args.SetServiceConfigJSON( "{\"healthCheckConfig\": " "{\"serviceName\": \"health_check_service_name\"}}"); - auto channel = BuildChannel("round_robin", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution({servers_[0]->port_}); + response_generator.SetNextResolution({servers_[0]->port_}); EXPECT_TRUE(WaitForChannelReady(channel.get())); CheckRpcSendOk(stub, DEBUG_LOCATION); } @@ -1267,9 +1322,10 @@ TEST_F(ClientLbEnd2endTest, RoundRobinWithHealthChecking) { args.SetServiceConfigJSON( "{\"healthCheckConfig\": " "{\"serviceName\": \"health_check_service_name\"}}"); - auto channel = BuildChannel("round_robin", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("round_robin", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); // Channel should not become READY, because health checks should be failing. gpr_log(GPR_INFO, "*** initial state: unknown health check service name for " @@ -1341,15 +1397,17 @@ TEST_F(ClientLbEnd2endTest, RoundRobinWithHealthCheckingInhibitPerChannel) { args.SetServiceConfigJSON( "{\"healthCheckConfig\": " "{\"serviceName\": \"health_check_service_name\"}}"); - auto channel1 = BuildChannel("round_robin", args); + auto response_generator1 = BuildResolverResponseGenerator(); + auto channel1 = BuildChannel("round_robin", response_generator1, args); auto stub1 = BuildStub(channel1); std::vector ports = GetServersPorts(); - SetNextResolution(ports); + response_generator1.SetNextResolution(ports); // Create a channel with health checking enabled but inhibited. args.SetInt(GRPC_ARG_INHIBIT_HEALTH_CHECKING, 1); - auto channel2 = BuildChannel("round_robin", args); + auto response_generator2 = BuildResolverResponseGenerator(); + auto channel2 = BuildChannel("round_robin", response_generator2, args); auto stub2 = BuildStub(channel2); - SetNextResolution(ports); + response_generator2.SetNextResolution(ports); // First channel should not become READY, because health checks should be // failing. EXPECT_FALSE(WaitForChannelReady(channel1.get(), 1)); @@ -1376,19 +1434,21 @@ TEST_F(ClientLbEnd2endTest, RoundRobinWithHealthCheckingServiceNamePerChannel) { args.SetServiceConfigJSON( "{\"healthCheckConfig\": " "{\"serviceName\": \"health_check_service_name\"}}"); - auto channel1 = BuildChannel("round_robin", args); + auto response_generator1 = BuildResolverResponseGenerator(); + auto channel1 = BuildChannel("round_robin", response_generator1, args); auto stub1 = BuildStub(channel1); std::vector ports = GetServersPorts(); - SetNextResolution(ports); + response_generator1.SetNextResolution(ports); // Create a channel with health-checking enabled with a different // service name. ChannelArguments args2; args2.SetServiceConfigJSON( "{\"healthCheckConfig\": " "{\"serviceName\": \"health_check_service_name2\"}}"); - auto channel2 = BuildChannel("round_robin", args2); + auto response_generator2 = BuildResolverResponseGenerator(); + auto channel2 = BuildChannel("round_robin", response_generator2, args2); auto stub2 = BuildStub(channel2); - SetNextResolution(ports); + response_generator2.SetNextResolution(ports); // Allow health checks from channel 2 to succeed. servers_[0]->SetServingStatus("health_check_service_name2", true); // First channel should not become READY, because health checks should be @@ -1438,9 +1498,11 @@ TEST_F(ClientLbInterceptTrailingMetadataTest, InterceptsRetriesDisabled) { const int kNumServers = 1; const int kNumRpcs = 10; StartServers(kNumServers); - auto channel = BuildChannel("intercept_trailing_metadata_lb"); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = + BuildChannel("intercept_trailing_metadata_lb", response_generator); auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); for (size_t i = 0; i < kNumRpcs; ++i) { CheckRpcSendOk(stub, DEBUG_LOCATION); } @@ -1470,9 +1532,11 @@ TEST_F(ClientLbInterceptTrailingMetadataTest, InterceptsRetriesEnabled) { " }\n" " } ]\n" "}"); - auto channel = BuildChannel("intercept_trailing_metadata_lb", args); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = + BuildChannel("intercept_trailing_metadata_lb", response_generator, args); auto stub = BuildStub(channel); - SetNextResolution(GetServersPorts()); + response_generator.SetNextResolution(GetServersPorts()); for (size_t i = 0; i < kNumRpcs; ++i) { CheckRpcSendOk(stub, DEBUG_LOCATION); } diff --git a/tools/distrib/python/bazel_deps.sh b/tools/distrib/python/bazel_deps.sh index 5f4ee1d6c4d..67896f10bd8 100755 --- a/tools/distrib/python/bazel_deps.sh +++ b/tools/distrib/python/bazel_deps.sh @@ -26,6 +26,7 @@ else docker build -t bazel_local_img tools/dockerfile/test/sanity docker run -v "$(realpath .):/src/grpc/:ro" \ -w /src/grpc/third_party/protobuf \ + --rm=true \ bazel_local_img \ bazel query 'deps('$1')' fi diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++ index 501be844410..bdb36ae1a7b 100644 --- a/tools/doxygen/Doxyfile.c++ +++ b/tools/doxygen/Doxyfile.c++ @@ -947,7 +947,9 @@ include/grpcpp/impl/channel_argument_option.h \ include/grpcpp/impl/client_unary_call.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ +include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ +include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -956,6 +958,7 @@ include/grpcpp/impl/codegen/call_op_set_interface.h \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ +include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -983,6 +986,7 @@ include/grpcpp/impl/codegen/rpc_service_method.h \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ +include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -995,6 +999,7 @@ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync.h \ include/grpcpp/impl/codegen/sync_stream.h \ +include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpcpp/impl/grpc_library.h \ include/grpcpp/impl/method_handler_impl.h \ @@ -1024,11 +1029,14 @@ include/grpcpp/server_impl.h \ include/grpcpp/server_posix.h \ include/grpcpp/server_posix_impl.h \ include/grpcpp/support/async_stream.h \ +include/grpcpp/support/async_stream_impl.h \ include/grpcpp/support/async_unary_call.h \ +include/grpcpp/support/async_unary_call_impl.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ include/grpcpp/support/channel_arguments_impl.h \ include/grpcpp/support/client_callback.h \ +include/grpcpp/support/client_callback_impl.h \ include/grpcpp/support/client_interceptor.h \ include/grpcpp/support/config.h \ include/grpcpp/support/interceptor.h \ @@ -1036,6 +1044,7 @@ include/grpcpp/support/message_allocator.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ include/grpcpp/support/server_callback.h \ +include/grpcpp/support/server_callback_impl.h \ include/grpcpp/support/server_interceptor.h \ include/grpcpp/support/slice.h \ include/grpcpp/support/status.h \ @@ -1043,6 +1052,7 @@ include/grpcpp/support/status_code_enum.h \ include/grpcpp/support/string_ref.h \ include/grpcpp/support/stub_options.h \ include/grpcpp/support/sync_stream.h \ +include/grpcpp/support/sync_stream_impl.h \ include/grpcpp/support/time.h \ include/grpcpp/support/validate_service_config.h diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 8254c5b58b1..0c33aad59d6 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -948,7 +948,9 @@ include/grpcpp/impl/channel_argument_option.h \ include/grpcpp/impl/client_unary_call.h \ include/grpcpp/impl/codegen/async_generic_service.h \ include/grpcpp/impl/codegen/async_stream.h \ +include/grpcpp/impl/codegen/async_stream_impl.h \ include/grpcpp/impl/codegen/async_unary_call.h \ +include/grpcpp/impl/codegen/async_unary_call_impl.h \ include/grpcpp/impl/codegen/byte_buffer.h \ include/grpcpp/impl/codegen/call.h \ include/grpcpp/impl/codegen/call_hook.h \ @@ -957,6 +959,7 @@ include/grpcpp/impl/codegen/call_op_set_interface.h \ include/grpcpp/impl/codegen/callback_common.h \ include/grpcpp/impl/codegen/channel_interface.h \ include/grpcpp/impl/codegen/client_callback.h \ +include/grpcpp/impl/codegen/client_callback_impl.h \ include/grpcpp/impl/codegen/client_context.h \ include/grpcpp/impl/codegen/client_context_impl.h \ include/grpcpp/impl/codegen/client_interceptor.h \ @@ -985,6 +988,7 @@ include/grpcpp/impl/codegen/rpc_service_method.h \ include/grpcpp/impl/codegen/security/auth_context.h \ include/grpcpp/impl/codegen/serialization_traits.h \ include/grpcpp/impl/codegen/server_callback.h \ +include/grpcpp/impl/codegen/server_callback_impl.h \ include/grpcpp/impl/codegen/server_context.h \ include/grpcpp/impl/codegen/server_context_impl.h \ include/grpcpp/impl/codegen/server_interceptor.h \ @@ -997,6 +1001,7 @@ include/grpcpp/impl/codegen/string_ref.h \ include/grpcpp/impl/codegen/stub_options.h \ include/grpcpp/impl/codegen/sync.h \ include/grpcpp/impl/codegen/sync_stream.h \ +include/grpcpp/impl/codegen/sync_stream_impl.h \ include/grpcpp/impl/codegen/time.h \ include/grpcpp/impl/grpc_library.h \ include/grpcpp/impl/method_handler_impl.h \ @@ -1026,11 +1031,14 @@ include/grpcpp/server_impl.h \ include/grpcpp/server_posix.h \ include/grpcpp/server_posix_impl.h \ include/grpcpp/support/async_stream.h \ +include/grpcpp/support/async_stream_impl.h \ include/grpcpp/support/async_unary_call.h \ +include/grpcpp/support/async_unary_call_impl.h \ include/grpcpp/support/byte_buffer.h \ include/grpcpp/support/channel_arguments.h \ include/grpcpp/support/channel_arguments_impl.h \ include/grpcpp/support/client_callback.h \ +include/grpcpp/support/client_callback_impl.h \ include/grpcpp/support/client_interceptor.h \ include/grpcpp/support/config.h \ include/grpcpp/support/interceptor.h \ @@ -1038,6 +1046,7 @@ include/grpcpp/support/message_allocator.h \ include/grpcpp/support/proto_buffer_reader.h \ include/grpcpp/support/proto_buffer_writer.h \ include/grpcpp/support/server_callback.h \ +include/grpcpp/support/server_callback_impl.h \ include/grpcpp/support/server_interceptor.h \ include/grpcpp/support/slice.h \ include/grpcpp/support/status.h \ @@ -1045,6 +1054,7 @@ include/grpcpp/support/status_code_enum.h \ include/grpcpp/support/string_ref.h \ include/grpcpp/support/stub_options.h \ include/grpcpp/support/sync_stream.h \ +include/grpcpp/support/sync_stream_impl.h \ include/grpcpp/support/time.h \ include/grpcpp/support/validate_service_config.h \ src/core/ext/filters/client_channel/health/health.pb.c \ diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index b098db5f3a9..2d3801b2128 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -10228,7 +10228,9 @@ "include/grpc++/impl/codegen/time.h", "include/grpcpp/impl/codegen/async_generic_service.h", "include/grpcpp/impl/codegen/async_stream.h", + "include/grpcpp/impl/codegen/async_stream_impl.h", "include/grpcpp/impl/codegen/async_unary_call.h", + "include/grpcpp/impl/codegen/async_unary_call_impl.h", "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", @@ -10237,6 +10239,7 @@ "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", "include/grpcpp/impl/codegen/client_callback.h", + "include/grpcpp/impl/codegen/client_callback_impl.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_context_impl.h", "include/grpcpp/impl/codegen/client_interceptor.h", @@ -10259,6 +10262,7 @@ "include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_callback_impl.h", "include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context_impl.h", "include/grpcpp/impl/codegen/server_interceptor.h", @@ -10270,6 +10274,7 @@ "include/grpcpp/impl/codegen/string_ref.h", "include/grpcpp/impl/codegen/stub_options.h", "include/grpcpp/impl/codegen/sync_stream.h", + "include/grpcpp/impl/codegen/sync_stream_impl.h", "include/grpcpp/impl/codegen/time.h" ], "is_filegroup": true, @@ -10308,7 +10313,9 @@ "include/grpc++/impl/codegen/time.h", "include/grpcpp/impl/codegen/async_generic_service.h", "include/grpcpp/impl/codegen/async_stream.h", + "include/grpcpp/impl/codegen/async_stream_impl.h", "include/grpcpp/impl/codegen/async_unary_call.h", + "include/grpcpp/impl/codegen/async_unary_call_impl.h", "include/grpcpp/impl/codegen/byte_buffer.h", "include/grpcpp/impl/codegen/call.h", "include/grpcpp/impl/codegen/call_hook.h", @@ -10317,6 +10324,7 @@ "include/grpcpp/impl/codegen/callback_common.h", "include/grpcpp/impl/codegen/channel_interface.h", "include/grpcpp/impl/codegen/client_callback.h", + "include/grpcpp/impl/codegen/client_callback_impl.h", "include/grpcpp/impl/codegen/client_context.h", "include/grpcpp/impl/codegen/client_context_impl.h", "include/grpcpp/impl/codegen/client_interceptor.h", @@ -10339,6 +10347,7 @@ "include/grpcpp/impl/codegen/security/auth_context.h", "include/grpcpp/impl/codegen/serialization_traits.h", "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_callback_impl.h", "include/grpcpp/impl/codegen/server_context.h", "include/grpcpp/impl/codegen/server_context_impl.h", "include/grpcpp/impl/codegen/server_interceptor.h", @@ -10350,6 +10359,7 @@ "include/grpcpp/impl/codegen/string_ref.h", "include/grpcpp/impl/codegen/stub_options.h", "include/grpcpp/impl/codegen/sync_stream.h", + "include/grpcpp/impl/codegen/sync_stream_impl.h", "include/grpcpp/impl/codegen/time.h" ], "third_party": false, @@ -10499,11 +10509,14 @@ "include/grpcpp/server_posix.h", "include/grpcpp/server_posix_impl.h", "include/grpcpp/support/async_stream.h", + "include/grpcpp/support/async_stream_impl.h", "include/grpcpp/support/async_unary_call.h", + "include/grpcpp/support/async_unary_call_impl.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", "include/grpcpp/support/channel_arguments_impl.h", "include/grpcpp/support/client_callback.h", + "include/grpcpp/support/client_callback_impl.h", "include/grpcpp/support/client_interceptor.h", "include/grpcpp/support/config.h", "include/grpcpp/support/interceptor.h", @@ -10511,6 +10524,7 @@ "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", "include/grpcpp/support/server_callback.h", + "include/grpcpp/support/server_callback_impl.h", "include/grpcpp/support/server_interceptor.h", "include/grpcpp/support/slice.h", "include/grpcpp/support/status.h", @@ -10518,6 +10532,7 @@ "include/grpcpp/support/string_ref.h", "include/grpcpp/support/stub_options.h", "include/grpcpp/support/sync_stream.h", + "include/grpcpp/support/sync_stream_impl.h", "include/grpcpp/support/time.h", "include/grpcpp/support/validate_service_config.h", "src/cpp/client/create_channel_internal.h", @@ -10627,11 +10642,14 @@ "include/grpcpp/server_posix.h", "include/grpcpp/server_posix_impl.h", "include/grpcpp/support/async_stream.h", + "include/grpcpp/support/async_stream_impl.h", "include/grpcpp/support/async_unary_call.h", + "include/grpcpp/support/async_unary_call_impl.h", "include/grpcpp/support/byte_buffer.h", "include/grpcpp/support/channel_arguments.h", "include/grpcpp/support/channel_arguments_impl.h", "include/grpcpp/support/client_callback.h", + "include/grpcpp/support/client_callback_impl.h", "include/grpcpp/support/client_interceptor.h", "include/grpcpp/support/config.h", "include/grpcpp/support/interceptor.h", @@ -10639,6 +10657,7 @@ "include/grpcpp/support/proto_buffer_reader.h", "include/grpcpp/support/proto_buffer_writer.h", "include/grpcpp/support/server_callback.h", + "include/grpcpp/support/server_callback_impl.h", "include/grpcpp/support/server_interceptor.h", "include/grpcpp/support/slice.h", "include/grpcpp/support/status.h", @@ -10646,6 +10665,7 @@ "include/grpcpp/support/string_ref.h", "include/grpcpp/support/stub_options.h", "include/grpcpp/support/sync_stream.h", + "include/grpcpp/support/sync_stream_impl.h", "include/grpcpp/support/time.h", "include/grpcpp/support/validate_service_config.h", "src/cpp/client/channel_cc.cc",